From fbeb5b9185baeda19e865228179e3e44c165f1d9 Mon Sep 17 00:00:00 2001 From: Lucas Fernandes Nogueira Date: Thu, 10 Aug 2023 06:12:38 -0700 Subject: [PATCH] refactor(core): use webview's URI schemes for IPC (#7170) Co-authored-by: chip --- .changes/channel-rust.md | 5 + .changes/ipc-custom-protocol.md | 6 + .changes/ipc-refactor.md | 6 + .changes/linux-ipc-body-feature.md | 5 + .changes/linux-protocol-body-feature.md | 5 + .changes/migrate-csp.md | 6 + .github/workflows/lint-core.yml | 2 +- .github/workflows/test-core.yml | 7 +- core/tauri-macros/src/command/wrapper.rs | 2 +- core/tauri-runtime-wry/Cargo.toml | 2 +- core/tauri-runtime-wry/src/lib.rs | 35 +- core/tauri-utils/src/pattern/isolation.js | 50 ++- core/tauri-utils/src/pattern/isolation.rs | 18 +- core/tauri/Cargo.toml | 3 +- core/tauri/build.rs | 5 + .../src/main/java/app/tauri/plugin/Invoke.kt | 3 +- .../java/app/tauri/plugin/PluginManager.kt | 12 +- .../ios-api/Sources/Tauri/Channel.swift | 4 +- .../mobile/ios-api/Sources/Tauri/Invoke.swift | 114 +++--- .../mobile/ios-api/Sources/Tauri/Tauri.swift | 214 +++++------ core/tauri/scripts/bundle.global.js | 2 +- core/tauri/scripts/core.js | 25 +- core/tauri/scripts/hotkey.js | 6 - core/tauri/scripts/ipc-protocol.js | 52 +++ core/tauri/scripts/ipc.js | 20 +- core/tauri/scripts/process-ipc-message-fn.js | 31 ++ .../tauri/scripts/stringify-ipc-message-fn.js | 17 - core/tauri/src/api/mod.rs | 2 - core/tauri/src/app.rs | 65 +++- core/tauri/src/command.rs | 113 +++--- core/tauri/src/event/commands.rs | 2 +- core/tauri/src/ios.rs | 32 +- core/tauri/src/ipc/channel.rs | 151 ++++++++ .../{api/ipc.rs => ipc/format_callback.rs} | 195 +--------- core/tauri/src/{hooks.rs => ipc/mod.rs} | 333 +++++++++++------ core/tauri/src/ipc/protocol.rs | 275 ++++++++++++++ core/tauri/src/jni_helpers.rs | 4 +- core/tauri/src/lib.rs | 32 +- core/tauri/src/manager.rs | 124 +++---- core/tauri/src/plugin.rs | 6 +- core/tauri/src/plugin/mobile.rs | 341 ++++++++++-------- core/tauri/src/scope/ipc.rs | 41 ++- core/tauri/src/state.rs | 3 +- core/tauri/src/test/mod.rs | 64 ++-- core/tauri/src/window.rs | 296 ++++++++------- .../test/fixture/src-tauri/tauri.conf.json | 4 +- examples/api/dist/assets/index.js | 14 +- examples/api/isolation-dist/index.js | 2 +- examples/api/src-tauri/Cargo.lock | 5 +- examples/api/src-tauri/src/cmd.rs | 13 +- examples/api/src-tauri/src/lib.rs | 6 +- .../java/com/plugin/sample/ExamplePlugin.kt | 5 + .../ios/Sources/ExamplePlugin.swift | 17 +- .../tauri-plugin-sample/src/desktop.rs | 6 + .../tauri-plugin-sample/src/models.rs | 5 +- examples/api/src-tauri/tauri.conf.json | 20 +- examples/commands/index.html | 4 +- examples/commands/main.rs | 13 +- examples/commands/tauri.conf.json | 12 +- examples/helloworld/tauri.conf.json | 12 +- examples/isolation/isolation-dist/index.js | 4 +- examples/isolation/tauri.conf.json | 4 +- examples/multiwindow/tauri.conf.json | 12 +- examples/navigation/tauri.conf.json | 4 +- examples/parent-window/tauri.conf.json | 12 +- examples/resources/src-tauri/tauri.conf.json | 16 +- examples/splashscreen/tauri.conf.json | 4 +- examples/state/tauri.conf.json | 12 +- examples/streaming/tauri.conf.json | 2 +- .../src-tauri/tauri.conf.json | 12 +- tooling/api/docs/js-api.json | 2 +- tooling/api/src/mocks.ts | 12 +- tooling/api/src/path.ts | 11 - tooling/api/src/tauri.ts | 41 ++- .../cpu_intensive/src-tauri/tauri.conf.json | 4 +- .../files_transfer/src-tauri/src/main.rs | 6 +- .../files_transfer/src-tauri/tauri.conf.json | 4 +- .../helloworld/src-tauri/tauri.conf.json | 4 +- tooling/cli/src/migrate/config.rs | 168 ++++++++- .../vanilla/src-tauri/tauri.conf.json | 4 +- 80 files changed, 2066 insertions(+), 1146 deletions(-) create mode 100644 .changes/channel-rust.md create mode 100644 .changes/ipc-custom-protocol.md create mode 100644 .changes/ipc-refactor.md create mode 100644 .changes/linux-ipc-body-feature.md create mode 100644 .changes/linux-protocol-body-feature.md create mode 100644 .changes/migrate-csp.md delete mode 100644 core/tauri/scripts/hotkey.js create mode 100644 core/tauri/scripts/ipc-protocol.js create mode 100644 core/tauri/scripts/process-ipc-message-fn.js delete mode 100644 core/tauri/scripts/stringify-ipc-message-fn.js create mode 100644 core/tauri/src/ipc/channel.rs rename core/tauri/src/{api/ipc.rs => ipc/format_callback.rs} (57%) rename core/tauri/src/{hooks.rs => ipc/mod.rs} (50%) create mode 100644 core/tauri/src/ipc/protocol.rs diff --git a/.changes/channel-rust.md b/.changes/channel-rust.md new file mode 100644 index 000000000000..d951507b2662 --- /dev/null +++ b/.changes/channel-rust.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:enhance +--- + +Added `Channel::new` allowing communication from a mobile plugin with Rust. diff --git a/.changes/ipc-custom-protocol.md b/.changes/ipc-custom-protocol.md new file mode 100644 index 000000000000..f3b99fb691b9 --- /dev/null +++ b/.changes/ipc-custom-protocol.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:enhance +"tauri-utils": patch:enhance +--- + +Use custom protocols on the IPC implementation to enhance performance. diff --git a/.changes/ipc-refactor.md b/.changes/ipc-refactor.md new file mode 100644 index 000000000000..6fd4527b56a4 --- /dev/null +++ b/.changes/ipc-refactor.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:breaking +"tauri-macros": patch:breaking +--- + +Moved `tauri::api::ipc` to `tauri::ipc` and refactored all types. diff --git a/.changes/linux-ipc-body-feature.md b/.changes/linux-ipc-body-feature.md new file mode 100644 index 000000000000..a80dbc0fdd35 --- /dev/null +++ b/.changes/linux-ipc-body-feature.md @@ -0,0 +1,5 @@ +--- +"tauri": patch:breaking +--- + +Removed the `linux-protocol-headers` feature (now always enabled) and added `linux-ipc-protocol`. diff --git a/.changes/linux-protocol-body-feature.md b/.changes/linux-protocol-body-feature.md new file mode 100644 index 000000000000..9542fbdd8a32 --- /dev/null +++ b/.changes/linux-protocol-body-feature.md @@ -0,0 +1,5 @@ +--- +"tauri-runtime-wry": patch:breaking +--- + +Removed the `linux-headers` feature (now always enabled) and added `linux-protocol-body`. diff --git a/.changes/migrate-csp.md b/.changes/migrate-csp.md new file mode 100644 index 000000000000..551c7c8473d2 --- /dev/null +++ b/.changes/migrate-csp.md @@ -0,0 +1,6 @@ +--- +"tauri-cli": patch:enhance +"@tauri-apps/cli": patch:enhance +--- + +Update migrate command to update the configuration CSP to include `ipc:` on the `connect-src` directive, needed by the new IPC using custom protocols. diff --git a/.github/workflows/lint-core.yml b/.github/workflows/lint-core.yml index 09dc4e09ce4a..3729dff688d3 100644 --- a/.github/workflows/lint-core.yml +++ b/.github/workflows/lint-core.yml @@ -50,7 +50,7 @@ jobs: clippy: - { args: '', key: 'empty' } - { - args: '--features compression,wry,linux-protocol-headers,isolation,custom-protocol,system-tray,test', + args: '--features compression,wry,isolation,custom-protocol,system-tray,test', key: 'all' } - { args: '--features custom-protocol', key: 'custom-protocol' } diff --git a/.github/workflows/test-core.yml b/.github/workflows/test-core.yml index 711eb5781f44..6b604201cd88 100644 --- a/.github/workflows/test-core.yml +++ b/.github/workflows/test-core.yml @@ -72,7 +72,7 @@ jobs: key: no-default } - { - args: --features compression,wry,linux-protocol-headers,isolation,custom-protocol,system-tray,test, + args: --features compression,wry,isolation,custom-protocol,system-tray,test, key: all } @@ -98,6 +98,11 @@ jobs: workspaces: core -> ../target save-if: ${{ matrix.features.key == 'all' }} + - name: Downgrade crates with MSRV conflict + # The --precise flag can only be used once per invocation. + run: | + cargo update -p time --precise 0.3.23 + - name: test uses: actions-rs/cargo@v1 with: diff --git a/core/tauri-macros/src/command/wrapper.rs b/core/tauri-macros/src/command/wrapper.rs index 19dbb2c379d8..fab592417eb4 100644 --- a/core/tauri-macros/src/command/wrapper.rs +++ b/core/tauri-macros/src/command/wrapper.rs @@ -211,7 +211,7 @@ pub fn wrapper(attributes: TokenStream, item: TokenStream) -> TokenStream { use #root::command::private::*; // prevent warnings when the body is a `compile_error!` or if the command has no arguments #[allow(unused_variables)] - let #root::Invoke { message: #message, resolver: #resolver } = $invoke; + let #root::ipc::Invoke { message: #message, resolver: #resolver } = $invoke; #body }}; diff --git a/core/tauri-runtime-wry/Cargo.toml b/core/tauri-runtime-wry/Cargo.toml index f231896e88a8..c6f5f38b1ca6 100644 --- a/core/tauri-runtime-wry/Cargo.toml +++ b/core/tauri-runtime-wry/Cargo.toml @@ -48,4 +48,4 @@ macos-private-api = [ "tauri-runtime/macos-private-api" ] objc-exception = [ "wry/objc-exception" ] -linux-headers = [ ] +linux-protocol-body = [ "wry/linux-body", "webkit2gtk/v2_40" ] diff --git a/core/tauri-runtime-wry/src/lib.rs b/core/tauri-runtime-wry/src/lib.rs index 083ad57d41b9..5a97faa151a5 100644 --- a/core/tauri-runtime-wry/src/lib.rs +++ b/core/tauri-runtime-wry/src/lib.rs @@ -3085,17 +3085,14 @@ fn create_webview( webview_attributes, uri_scheme_protocols, mut window_builder, - ipc_handler, label, + ipc_handler, url, menu_ids, #[cfg(target_os = "android")] on_webview_created, .. } = pending; - let webview_id_map = context.webview_id_map.clone(); - #[cfg(windows)] - let proxy = context.proxy.clone(); let window_event_listeners = WindowEventListeners::default(); @@ -3108,6 +3105,8 @@ fn create_webview( #[cfg(windows)] let window_theme = window_builder.inner.window.preferred_theme; + #[cfg(windows)] + let proxy = context.proxy.clone(); #[cfg(target_os = "macos")] { @@ -3130,7 +3129,7 @@ fn create_webview( }; let window = window_builder.inner.build(event_loop).unwrap(); - webview_id_map.insert(window.id(), window_id); + context.webview_id_map.insert(window.id(), window_id); if window_builder.center { let _ = center_window(&window, window.inner_size()); @@ -3157,17 +3156,18 @@ fn create_webview( } #[cfg(windows)] - if let Some(additional_browser_args) = webview_attributes.additional_browser_args { - webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args); - } + { + if let Some(additional_browser_args) = webview_attributes.additional_browser_args { + webview_builder = webview_builder.with_additional_browser_args(&additional_browser_args); + } - #[cfg(windows)] - if let Some(theme) = window_theme { - webview_builder = webview_builder.with_theme(match theme { - WryTheme::Dark => wry::webview::Theme::Dark, - WryTheme::Light => wry::webview::Theme::Light, - _ => wry::webview::Theme::Light, - }); + if let Some(theme) = window_theme { + webview_builder = webview_builder.with_theme(match theme { + WryTheme::Dark => wry::webview::Theme::Dark, + WryTheme::Light => wry::webview::Theme::Light, + _ => wry::webview::Theme::Light, + }); + } } if let Some(handler) = ipc_handler { @@ -3178,6 +3178,7 @@ fn create_webview( handler, )); } + for (scheme, protocol) in uri_scheme_protocols { webview_builder = webview_builder.with_custom_protocol(scheme, move |wry_request| { protocol(&HttpRequestWrapper::from(wry_request).0) @@ -3254,7 +3255,7 @@ fn create_webview( unsafe { controller.add_GotFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { - let _ = proxy_.send_event(Message::Webview( + let _ = proxy.send_event(Message::Webview( window_id, WebviewMessage::WebviewEvent(WebviewEvent::Focused(true)), )); @@ -3267,7 +3268,7 @@ fn create_webview( unsafe { controller.add_LostFocus( &FocusChangedEventHandler::create(Box::new(move |_, _| { - let _ = proxy.send_event(Message::Webview( + let _ = proxy_.send_event(Message::Webview( window_id, WebviewMessage::WebviewEvent(WebviewEvent::Focused(false)), )); diff --git a/core/tauri-utils/src/pattern/isolation.js b/core/tauri-utils/src/pattern/isolation.js index ce284d59a1da..e236839aabb0 100644 --- a/core/tauri-utils/src/pattern/isolation.js +++ b/core/tauri-utils/src/pattern/isolation.js @@ -8,7 +8,8 @@ * isolation frame -> main frame = isolation message */ -;(async function () { +; +(async function () { /** * Sends the message to the isolation frame. * @param {any} message @@ -38,34 +39,52 @@ * @return {Promise<{nonce: number[], payload: number[]}>} */ async function encrypt(data) { - let algorithm = Object.create(null) + const algorithm = Object.create(null) algorithm.name = 'AES-GCM' algorithm.iv = window.crypto.getRandomValues(new Uint8Array(12)) - let encoder = new TextEncoder() - let payloadRaw = encoder.encode(__RAW_stringify_ipc_message_fn__(data)) + const encoder = new TextEncoder() + const encoded = encoder.encode(__RAW_process_ipc_message_fn__(data).data) return window.crypto.subtle - .encrypt(algorithm, aesGcmKey, payloadRaw) + .encrypt(algorithm, aesGcmKey, encoded) .then((payload) => { - let result = Object.create(null) + const result = Object.create(null) result.nonce = Array.from(new Uint8Array(algorithm.iv)) result.payload = Array.from(new Uint8Array(payload)) return result }) } + /** + * Detects if a message event is a valid isolation message. + * + * @param {MessageEvent} event - a message event that is expected to be an isolation message + * @return {boolean} - if the event was a valid isolation message + */ + function isIsolationMessage(data) { + if (typeof data === 'object' && typeof data.payload === 'object') { + const keys = data.payload ? Object.keys(data.payload) : [] + return ( + keys.length > 0 && + keys.every((key) => key === 'nonce' || key === 'payload') + ) + } + return false + } + /** * Detect if a message event is a valid isolation payload. * * @param {MessageEvent} event - a message event that is expected to be an isolation payload * @return boolean */ - function isIsolationPayload(event) { + function isIsolationPayload(data) { return ( - typeof event.data === 'object' && - 'callback' in event.data && - 'error' in event.data + typeof data === 'object' && + 'callback' in data && + 'error' in data && + !isIsolationMessage(data) ) } @@ -74,7 +93,7 @@ * @param {MessageEvent} event */ async function payloadHandler(event) { - if (!isIsolationPayload(event)) { + if (!isIsolationPayload(event.data)) { return } @@ -85,8 +104,13 @@ data = await window.__TAURI_ISOLATION_HOOK__(data) } - const encrypted = await encrypt(data) - sendMessage(encrypted) + const message = Object.create(null) + message.cmd = data.cmd + message.callback = data.callback + message.error = data.error + message.options = data.options + message.payload = await encrypt(data.payload) + sendMessage(message) } window.addEventListener('message', payloadHandler, false) diff --git a/core/tauri-utils/src/pattern/isolation.rs b/core/tauri-utils/src/pattern/isolation.rs index ae5382450320..3b2dfc0846ce 100644 --- a/core/tauri-utils/src/pattern/isolation.rs +++ b/core/tauri-utils/src/pattern/isolation.rs @@ -96,16 +96,14 @@ impl Keys { } /// Decrypts a message using the generated keys. - pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result { + pub fn decrypt(&self, raw: RawIsolationPayload<'_>) -> Result, Error> { let RawIsolationPayload { nonce, payload } = raw; let nonce: [u8; 12] = nonce.as_ref().try_into()?; - let bytes = self + self .aes_gcm .key .decrypt(Nonce::from_slice(&nonce), payload.as_ref()) - .map_err(|_| self::Error::Aes)?; - - String::from_utf8(bytes).map_err(Into::into) + .map_err(|_| self::Error::Aes) } } @@ -116,11 +114,11 @@ pub struct RawIsolationPayload<'a> { payload: Cow<'a, [u8]>, } -impl<'a> TryFrom<&'a str> for RawIsolationPayload<'a> { +impl<'a> TryFrom<&'a Vec> for RawIsolationPayload<'a> { type Error = Error; - fn try_from(value: &'a str) -> Result { - serde_json::from_str(value).map_err(Into::into) + fn try_from(value: &'a Vec) -> Result { + serde_json::from_slice(value).map_err(Into::into) } } @@ -141,9 +139,9 @@ pub struct IsolationJavascriptCodegen { pub struct IsolationJavascriptRuntime<'a> { /// The key used on the Rust backend and the Isolation Javascript pub runtime_aes_gcm_key: &'a [u8; 32], - /// The function that stringifies a IPC message. + /// The function that processes the IPC message. #[raw] - pub stringify_ipc_message_fn: &'a str, + pub process_ipc_message_fn: &'a str, } #[cfg(test)] diff --git a/core/tauri/Cargo.toml b/core/tauri/Cargo.toml index ef6e3309cdba..fa93ba5f9b59 100644 --- a/core/tauri/Cargo.toml +++ b/core/tauri/Cargo.toml @@ -61,6 +61,7 @@ reqwest = { version = "0.11", default-features = false, features = [ "json", "st bytes = { version = "1", features = [ "serde" ] } raw-window-handle = "0.5" glob = "0.3" +mime = "0.3" data-url = { version = "0.2", optional = true } serialize-to-javascript = "=0.1.1" infer = { version = "0.9", optional = true } @@ -118,7 +119,7 @@ test = [ ] compression = [ "tauri-macros/compression", "tauri-utils/compression" ] wry = [ "tauri-runtime-wry" ] objc-exception = [ "tauri-runtime-wry/objc-exception" ] -linux-protocol-headers = [ "tauri-runtime-wry/linux-headers", "webkit2gtk/v2_36" ] +linux-ipc-protocol = [ "tauri-runtime-wry/linux-protocol-body", "webkit2gtk/v2_40" ] isolation = [ "tauri-utils/isolation", "tauri-macros/isolation" ] custom-protocol = [ "tauri-macros/custom-protocol" ] native-tls = [ "reqwest/native-tls" ] diff --git a/core/tauri/build.rs b/core/tauri/build.rs index 29ea69f97a24..669de4270fdf 100644 --- a/core/tauri/build.rs +++ b/core/tauri/build.rs @@ -50,6 +50,11 @@ fn main() { alias("desktop", !mobile); alias("mobile", mobile); + alias( + "ipc_custom_protocol", + target_os != "android" && (target_os != "linux" || has_feature("linux-ipc-protocol")), + ); + let checked_features_out_path = Path::new(&var("OUT_DIR").unwrap()).join("checked_features"); std::fs::write( checked_features_out_path, diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt index 4bfb5bd908e0..3f0ea12146c3 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt @@ -14,6 +14,7 @@ class Invoke( val callback: Long, val error: Long, private val sendResponse: (callback: Long, data: PluginResult?) -> Unit, + private val sendChannelData: (channelId: Long, data: PluginResult) -> Unit, val data: JSObject) { fun resolve(data: JSObject?) { @@ -205,6 +206,6 @@ class Invoke( fun getChannel(name: String): Channel? { val channelDef = getString(name, "") val callback = channelDef.substring(CHANNEL_PREFIX.length).toLongOrNull() ?: return null - return Channel(callback) { res -> sendResponse(callback, PluginResult(res)) } + return Channel(callback) { res -> sendChannelData(callback, PluginResult(res)) } } } diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt index 7dbab66429f8..62ddc8d19d5a 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/PluginManager.kt @@ -85,15 +85,6 @@ class PluginManager(val activity: AppCompatActivity) { } } - @JniMethod - fun postIpcMessage(webView: WebView, pluginId: String, command: String, data: JSObject, callback: Long, error: Long) { - val invoke = Invoke(callback, command, callback, error, { fn, result -> - webView.evaluateJavascript("window['_$fn']($result)", null) - }, data) - - dispatchPluginMessage(invoke, pluginId) - } - @JniMethod fun runCommand(id: Int, pluginId: String, command: String, data: JSObject) { val successId = 0L @@ -107,6 +98,8 @@ class PluginManager(val activity: AppCompatActivity) { error = result } handlePluginResponse(id, success?.toString(), error?.toString()) + }, { channelId, payload -> + sendChannelData(channelId, payload.toString()) }, data) dispatchPluginMessage(invoke, pluginId) @@ -140,4 +133,5 @@ class PluginManager(val activity: AppCompatActivity) { } private external fun handlePluginResponse(id: Int, success: String?, error: String?) + private external fun sendChannelData(id: Long, data: String) } diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift index 6722caec0f24..f3d03ba662b7 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Channel.swift @@ -6,8 +6,8 @@ public class Channel { public let id: UInt64 let handler: (JsonValue) -> Void - public init(callback: UInt64, handler: @escaping (JsonValue) -> Void) { - self.id = callback + public init(id: UInt64, handler: @escaping (JsonValue) -> Void) { + self.id = id self.handler = handler } diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift index 15ffb7ac2ff4..f7490ae35bdc 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift @@ -8,76 +8,92 @@ import UIKit let CHANNEL_PREFIX = "__CHANNEL__:" @objc public class Invoke: NSObject, JSValueContainer, BridgedJSValueContainer { - public var dictionaryRepresentation: NSDictionary { - return data as NSDictionary - } + public var dictionaryRepresentation: NSDictionary { + return data as NSDictionary + } - public static var jsDateFormatter: ISO8601DateFormatter = { - return ISO8601DateFormatter() - }() + public static var jsDateFormatter: ISO8601DateFormatter = { + return ISO8601DateFormatter() + }() public var command: String var callback: UInt64 var error: UInt64 - public var data: JSObject - var sendResponse: (UInt64, JsonValue?) -> Void + public var data: JSObject + var sendResponse: (UInt64, JsonValue?) -> Void + var sendChannelData: (UInt64, JsonValue) -> Void - public init(command: String, callback: UInt64, error: UInt64, sendResponse: @escaping (UInt64, JsonValue?) -> Void, data: JSObject?) { + public init( + command: String, callback: UInt64, error: UInt64, + sendResponse: @escaping (UInt64, JsonValue?) -> Void, + sendChannelData: @escaping (UInt64, JsonValue) -> Void, data: JSObject? + ) { self.command = command self.callback = callback self.error = error - self.data = data ?? [:] - self.sendResponse = sendResponse - } + self.data = data ?? [:] + self.sendResponse = sendResponse + self.sendChannelData = sendChannelData + } - public func resolve() { - sendResponse(callback, nil) - } + public func resolve() { + sendResponse(callback, nil) + } - public func resolve(_ data: JsonObject) { - resolve(.dictionary(data)) - } + public func resolve(_ data: JsonObject) { + resolve(.dictionary(data)) + } - public func resolve(_ data: JsonValue) { - sendResponse(callback, data) - } + public func resolve(_ data: JsonValue) { + sendResponse(callback, data) + } - public func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: JsonValue? = nil) { - let payload: NSMutableDictionary = ["message": message, "code": code ?? "", "error": error ?? ""] - if let data = data { - switch data { - case .dictionary(let dict): - for entry in dict { - payload[entry.key] = entry.value - } - } - } - sendResponse(self.error, .dictionary(payload as! JsonObject)) - } + public func reject( + _ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: JsonValue? = nil + ) { + let payload: NSMutableDictionary = [ + "message": message, "code": code ?? "", "error": error ?? "", + ] + if let data = data { + switch data { + case .dictionary(let dict): + for entry in dict { + payload[entry.key] = entry.value + } + } + } + sendResponse(self.error, .dictionary(payload as! JsonObject)) + } - public func unimplemented() { - unimplemented("not implemented") - } + public func unimplemented() { + unimplemented("not implemented") + } - public func unimplemented(_ message: String) { - sendResponse(error, .dictionary(["message": message])) - } + public func unimplemented(_ message: String) { + sendResponse(error, .dictionary(["message": message])) + } - public func unavailable() { - unavailable("not available") - } + public func unavailable() { + unavailable("not available") + } - public func unavailable(_ message: String) { - sendResponse(error, .dictionary(["message": message])) - } + public func unavailable(_ message: String) { + sendResponse(error, .dictionary(["message": message])) + } public func getChannel(_ key: String) -> Channel? { let channelDef = getString(key, "") - guard let callback = UInt64(channelDef.components(separatedBy: CHANNEL_PREFIX)[1]) else { + let components = channelDef.components(separatedBy: CHANNEL_PREFIX) + if components.count < 2 { + return nil + } + guard let channelId = UInt64(components[1]) else { return nil } - return Channel(callback: callback, handler: { (res: JsonValue) -> Void in - self.sendResponse(callback, res) - }) + return Channel( + id: channelId, + handler: { (res: JsonValue) -> Void in + self.sendChannelData(channelId, res) + }) } } diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift index b5f4ec6531e8..80a80fab764d 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Tauri.swift @@ -2,145 +2,147 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -import SwiftRs import Foundation +import SwiftRs import UIKit import WebKit import os.log class PluginHandle { - var instance: Plugin - var loaded = false + var instance: Plugin + var loaded = false - init(plugin: Plugin) { - instance = plugin - } + init(plugin: Plugin) { + instance = plugin + } } public class PluginManager { - static let shared: PluginManager = PluginManager() - public var viewController: UIViewController? - var plugins: [String: PluginHandle] = [:] - var ipcDispatchQueue = DispatchQueue(label: "ipc") - public var isSimEnvironment: Bool { - #if targetEnvironment(simulator) - return true - #else - return false - #endif - } + static let shared: PluginManager = PluginManager() + public var viewController: UIViewController? + var plugins: [String: PluginHandle] = [:] + var ipcDispatchQueue = DispatchQueue(label: "ipc") + public var isSimEnvironment: Bool { + #if targetEnvironment(simulator) + return true + #else + return false + #endif + } - public func assetUrl(fromLocalURL url: URL?) -> URL? { - guard let inputURL = url else { - return nil - } + public func assetUrl(fromLocalURL url: URL?) -> URL? { + guard let inputURL = url else { + return nil + } - return URL(string: "asset://localhost")!.appendingPathComponent(inputURL.path) - } + return URL(string: "asset://localhost")!.appendingPathComponent(inputURL.path) + } - func onWebviewCreated(_ webview: WKWebView) { - for (_, handle) in plugins { - if (!handle.loaded) { - handle.instance.load(webview: webview) - } - } - } + func onWebviewCreated(_ webview: WKWebView) { + for (_, handle) in plugins { + if !handle.loaded { + handle.instance.load(webview: webview) + } + } + } - func load(name: String, plugin: P, config: JSObject, webview: WKWebView?) { + func load(name: String, plugin: P, config: JSObject, webview: WKWebView?) { plugin.setConfig(config) - let handle = PluginHandle(plugin: plugin) - if let webview = webview { - handle.instance.load(webview: webview) - handle.loaded = true - } - plugins[name] = handle - } + let handle = PluginHandle(plugin: plugin) + if let webview = webview { + handle.instance.load(webview: webview) + handle.loaded = true + } + plugins[name] = handle + } - func invoke(name: String, invoke: Invoke) { - if let plugin = plugins[name] { - ipcDispatchQueue.async { - let selectorWithThrows = Selector(("\(invoke.command):error:")) - if plugin.instance.responds(to: selectorWithThrows) { - var error: NSError? = nil - withUnsafeMutablePointer(to: &error) { - let methodIMP: IMP! = plugin.instance.method(for: selectorWithThrows) - unsafeBitCast(methodIMP, to: (@convention(c)(Any?, Selector, Invoke, OpaquePointer) -> Void).self)(plugin.instance, selectorWithThrows, invoke, OpaquePointer($0)) - } - if let error = error { - invoke.reject("\(error)") - // TODO: app crashes without this leak - let _ = Unmanaged.passRetained(error) - } - } else { - let selector = Selector(("\(invoke.command):")) - if plugin.instance.responds(to: selector) { - plugin.instance.perform(selector, with: invoke) - } else { - invoke.reject("No command \(invoke.command) found for plugin \(name)") - } - } - } - } else { - invoke.reject("Plugin \(name) not initialized") - } - } + func invoke(name: String, invoke: Invoke) { + if let plugin = plugins[name] { + ipcDispatchQueue.async { + let selectorWithThrows = Selector(("\(invoke.command):error:")) + if plugin.instance.responds(to: selectorWithThrows) { + var error: NSError? = nil + withUnsafeMutablePointer(to: &error) { + let methodIMP: IMP! = plugin.instance.method(for: selectorWithThrows) + unsafeBitCast( + methodIMP, to: (@convention(c) (Any?, Selector, Invoke, OpaquePointer) -> Void).self)( + plugin.instance, selectorWithThrows, invoke, OpaquePointer($0)) + } + if let error = error { + invoke.reject("\(error)") + // TODO: app crashes without this leak + let _ = Unmanaged.passRetained(error) + } + } else { + let selector = Selector(("\(invoke.command):")) + if plugin.instance.responds(to: selector) { + plugin.instance.perform(selector, with: invoke) + } else { + invoke.reject("No command \(invoke.command) found for plugin \(name)") + } + } + } + } else { + invoke.reject("Plugin \(name) not initialized") + } + } } extension PluginManager: NSCopying { - public func copy(with zone: NSZone? = nil) -> Any { - return self - } + public func copy(with zone: NSZone? = nil) -> Any { + return self + } } @_cdecl("register_plugin") func registerPlugin(name: SRString, plugin: NSObject, config: NSDictionary?, webview: WKWebView?) { - PluginManager.shared.load( - name: name.toString(), - plugin: plugin as! Plugin, + PluginManager.shared.load( + name: name.toString(), + plugin: plugin as! Plugin, config: JSTypes.coerceDictionaryToJSObject(config ?? [:], formattingDatesAsStrings: true)!, webview: webview - ) + ) } @_cdecl("on_webview_created") func onWebviewCreated(webview: WKWebView, viewController: UIViewController) { - PluginManager.shared.viewController = viewController - PluginManager.shared.onWebviewCreated(webview) -} - -@_cdecl("post_ipc_message") -func postIpcMessage(webview: WKWebView, name: SRString, command: SRString, data: NSDictionary, callback: UInt64, error: UInt64) { - let invoke = Invoke(command: command.toString(), callback: callback, error: error, sendResponse: { (fn: UInt64, payload: JsonValue?) -> Void in - var payloadJson: String - do { - try payloadJson = payload == nil ? "null" : payload!.jsonRepresentation() ?? "`Failed to serialize payload`" - } catch { - payloadJson = "`\(error)`" - } - webview.evaluateJavaScript("window['_\(fn)'](\(payloadJson))") - }, data: JSTypes.coerceDictionaryToJSObject(data, formattingDatesAsStrings: true)) - PluginManager.shared.invoke(name: name.toString(), invoke: invoke) + PluginManager.shared.viewController = viewController + PluginManager.shared.onWebviewCreated(webview) } -@_cdecl("run_plugin_method") +@_cdecl("run_plugin_command") func runCommand( - id: Int, - name: SRString, - command: SRString, - data: NSDictionary, - callback: @escaping @convention(c) (Int, Bool, UnsafePointer?) -> Void + id: Int, + name: SRString, + command: SRString, + data: NSDictionary, + callback: @escaping @convention(c) (Int, Bool, UnsafePointer?) -> Void, + sendChannelData: @escaping @convention(c) (UInt64, UnsafePointer) -> Void ) { let callbackId: UInt64 = 0 let errorId: UInt64 = 1 - let invoke = Invoke(command: command.toString(), callback: callbackId, error: errorId, sendResponse: { (fn: UInt64, payload: JsonValue?) -> Void in - let success = fn == callbackId - var payloadJson: String = "" - do { - try payloadJson = payload == nil ? "null" : payload!.jsonRepresentation() ?? "`Failed to serialize payload`" - } catch { - payloadJson = "`\(error)`" - } - callback(id, success, payloadJson.cString(using: String.Encoding.utf8)) - }, data: JSTypes.coerceDictionaryToJSObject(data, formattingDatesAsStrings: true)) - PluginManager.shared.invoke(name: name.toString(), invoke: invoke) + let invoke = Invoke( + command: command.toString(), callback: callbackId, error: errorId, + sendResponse: { (fn: UInt64, payload: JsonValue?) -> Void in + let success = fn == callbackId + var payloadJson: String = "" + do { + try payloadJson = + payload == nil ? "null" : payload!.jsonRepresentation() ?? "`Failed to serialize payload`" + } catch { + payloadJson = "`\(error)`" + } + callback(id, success, payloadJson.cString(using: String.Encoding.utf8)) + }, + sendChannelData: { (id: UInt64, payload: JsonValue) -> Void in + var payloadJson: String = "" + do { + try payloadJson = + payload.jsonRepresentation() ?? "`Failed to serialize payload`" + } catch { + payloadJson = "`\(error)`" + } + sendChannelData(id, payloadJson) + }, data: JSTypes.coerceDictionaryToJSObject(data, formattingDatesAsStrings: true)) + PluginManager.shared.invoke(name: name.toString(), invoke: invoke) } diff --git a/core/tauri/scripts/bundle.global.js b/core/tauri/scripts/bundle.global.js index 3ae27b270d74..299376ccf66e 100644 --- a/core/tauri/scripts/bundle.global.js +++ b/core/tauri/scripts/bundle.global.js @@ -1,2 +1,2 @@ -"use strict";var __TAURI_IIFE__=(()=>{var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var O=Object.prototype.hasOwnProperty;var p=(n,e)=>{for(var i in e)m(n,i,{get:e[i],enumerable:!0})},W=(n,e,i,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of E(e))!O.call(n,a)&&a!==i&&m(n,a,{get:()=>e[a],enumerable:!(o=C(e,a))||o.enumerable});return n};var N=n=>W(m({},"__esModule",{value:!0}),n);var P=(n,e,i)=>{if(!e.has(n))throw TypeError("Cannot "+i)};var _=(n,e,i)=>(P(n,e,"read from private field"),i?i.call(n):e.get(n)),D=(n,e,i)=>{if(e.has(n))throw TypeError("Cannot add the same private member more than once");e instanceof WeakSet?e.add(n):e.set(n,i)},w=(n,e,i,o)=>(P(n,e,"write to private field"),o?o.call(n,i):e.set(n,i),i);var hn={};p(hn,{event:()=>f,invoke:()=>fn,path:()=>h,tauri:()=>y});var f={};p(f,{TauriEvent:()=>b,emit:()=>F,listen:()=>I,once:()=>U});var y={};p(y,{Channel:()=>l,PluginListener:()=>g,addPluginListener:()=>L,convertFileSrc:()=>k,invoke:()=>t,transformCallback:()=>u});function T(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function u(n,e=!1){let i=T(),o=`_${i}`;return Object.defineProperty(window,o,{value:a=>(e&&Reflect.deleteProperty(window,o),n?.(a)),writable:!1,configurable:!0}),i}var c,l=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0;D(this,c,()=>{});this.id=u(e=>{_(this,c).call(this,e)})}set onmessage(e){w(this,c,e)}get onmessage(){return _(this,c)}toJSON(){return`__CHANNEL__:${this.id}`}};c=new WeakMap;var g=class{constructor(e,i,o){this.plugin=e,this.event=i,this.channelId=o}async unregister(){return t(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function L(n,e,i){let o=new l;return o.onmessage=i,t(`plugin:${n}|register_listener`,{event:e,handler:o}).then(()=>new g(n,e,o.id))}async function t(n,e={}){return new Promise((i,o)=>{let a=u(d=>{i(d),Reflect.deleteProperty(window,`_${v}`)},!0),v=u(d=>{o(d),Reflect.deleteProperty(window,`_${a}`)},!0);window.__TAURI_IPC__({cmd:n,callback:a,error:v,...e})})}function k(n,e="asset"){let i=encodeURIComponent(n);return navigator.userAgent.includes("Windows")?`https://${e}.localhost/${i}`:`${e}://localhost/${i}`}var b=(s=>(s.WINDOW_RESIZED="tauri://resize",s.WINDOW_MOVED="tauri://move",s.WINDOW_CLOSE_REQUESTED="tauri://close-requested",s.WINDOW_CREATED="tauri://window-created",s.WINDOW_DESTROYED="tauri://destroyed",s.WINDOW_FOCUS="tauri://focus",s.WINDOW_BLUR="tauri://blur",s.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",s.WINDOW_THEME_CHANGED="tauri://theme-changed",s.WINDOW_FILE_DROP="tauri://file-drop",s.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",s.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",s.MENU="tauri://menu",s))(b||{});async function A(n,e){await t("plugin:event|unlisten",{event:n,eventId:e})}async function I(n,e,i){return t("plugin:event|listen",{event:n,windowLabel:i?.target,handler:u(e)}).then(o=>async()=>A(n,o))}async function U(n,e,i){return I(n,o=>{e(o),A(n,o.id).catch(()=>{})},i)}async function F(n,e,i){await t("plugin:event|emit",{event:n,windowLabel:i?.target,payload:e})}var h={};p(h,{BaseDirectory:()=>R,appCacheDir:()=>S,appConfigDir:()=>x,appDataDir:()=>$,appLocalDataDir:()=>H,appLogDir:()=>sn,audioDir:()=>M,basename:()=>_n,cacheDir:()=>V,configDir:()=>j,dataDir:()=>z,delimiter:()=>un,desktopDir:()=>G,dirname:()=>dn,documentDir:()=>q,downloadDir:()=>J,executableDir:()=>K,extname:()=>mn,fontDir:()=>Q,homeDir:()=>Y,isAbsolute:()=>yn,join:()=>gn,localDataDir:()=>Z,normalize:()=>ln,pictureDir:()=>X,publicDir:()=>B,resolve:()=>pn,resolveResource:()=>en,resourceDir:()=>nn,runtimeDir:()=>rn,sep:()=>cn,tempDir:()=>an,templateDir:()=>tn,videoDir:()=>on});var R=(r=>(r[r.Audio=1]="Audio",r[r.Cache=2]="Cache",r[r.Config=3]="Config",r[r.Data=4]="Data",r[r.LocalData=5]="LocalData",r[r.Document=6]="Document",r[r.Download=7]="Download",r[r.Picture=8]="Picture",r[r.Public=9]="Public",r[r.Video=10]="Video",r[r.Resource=11]="Resource",r[r.Temp=12]="Temp",r[r.AppConfig=13]="AppConfig",r[r.AppData=14]="AppData",r[r.AppLocalData=15]="AppLocalData",r[r.AppCache=16]="AppCache",r[r.AppLog=17]="AppLog",r[r.Desktop=18]="Desktop",r[r.Executable=19]="Executable",r[r.Font=20]="Font",r[r.Home=21]="Home",r[r.Runtime=22]="Runtime",r[r.Template=23]="Template",r))(R||{});async function x(){return t("plugin:path|resolve_directory",{directory:13})}async function $(){return t("plugin:path|resolve_directory",{directory:14})}async function H(){return t("plugin:path|resolve_directory",{directory:15})}async function S(){return t("plugin:path|resolve_directory",{directory:16})}async function M(){return t("plugin:path|resolve_directory",{directory:1})}async function V(){return t("plugin:path|resolve_directory",{directory:2})}async function j(){return t("plugin:path|resolve_directory",{directory:3})}async function z(){return t("plugin:path|resolve_directory",{directory:4})}async function G(){return t("plugin:path|resolve_directory",{directory:18})}async function q(){return t("plugin:path|resolve_directory",{directory:6})}async function J(){return t("plugin:path|resolve_directory",{directory:7})}async function K(){return t("plugin:path|resolve_directory",{directory:19})}async function Q(){return t("plugin:path|resolve_directory",{directory:20})}async function Y(){return t("plugin:path|resolve_directory",{directory:21})}async function Z(){return t("plugin:path|resolve_directory",{directory:5})}async function X(){return t("plugin:path|resolve_directory",{directory:8})}async function B(){return t("plugin:path|resolve_directory",{directory:9})}async function nn(){return t("plugin:path|resolve_directory",{directory:11})}async function en(n){return t("plugin:path|resolve_directory",{directory:11,path:n})}async function rn(){return t("plugin:path|resolve_directory",{directory:22})}async function tn(){return t("plugin:path|resolve_directory",{directory:23})}async function on(){return t("plugin:path|resolve_directory",{directory:10})}async function sn(){return t("plugin:path|resolve_directory",{directory:17})}async function an(n){return t("plugin:path|resolve_directory",{directory:12})}function cn(){return window.__TAURI__.path.__sep}function un(){return window.__TAURI__.path.__delimiter}async function pn(...n){return t("plugin:path|resolve",{paths:n})}async function ln(n){return t("plugin:path|normalize",{path:n})}async function gn(...n){return t("plugin:path|join",{paths:n})}async function dn(n){return t("plugin:path|dirname",{path:n})}async function mn(n){return t("plugin:path|extname",{path:n})}async function _n(n,e){return t("plugin:path|basename",{path:n,ext:e})}async function yn(n){return t("plugin:path|isAbsolute",{path:n})}var fn=t;return N(hn);})(); +"use strict";var __TAURI_IIFE__=(()=>{var m=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var p=(n,r)=>{for(var i in r)m(n,i,{get:r[i],enumerable:!0})},W=(n,r,i,o)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of E(r))!N.call(n,a)&&a!==i&&m(n,a,{get:()=>r[a],enumerable:!(o=C(r,a))||o.enumerable});return n};var k=n=>W(m({},"__esModule",{value:!0}),n);var D=(n,r,i)=>{if(!r.has(n))throw TypeError("Cannot "+i)};var _=(n,r,i)=>(D(n,r,"read from private field"),i?i.call(n):r.get(n)),w=(n,r,i)=>{if(r.has(n))throw TypeError("Cannot add the same private member more than once");r instanceof WeakSet?r.add(n):r.set(n,i)},A=(n,r,i,o)=>(D(n,r,"write to private field"),o?o.call(n,i):r.set(n,i),i);var vn={};p(vn,{event:()=>f,invoke:()=>hn,path:()=>h,tauri:()=>y});var f={};p(f,{TauriEvent:()=>b,emit:()=>x,listen:()=>R,once:()=>F});var y={};p(y,{Channel:()=>l,PluginListener:()=>g,addPluginListener:()=>L,convertFileSrc:()=>U,invoke:()=>t,transformCallback:()=>u});function T(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function u(n,r=!1){let i=T(),o=`_${i}`;return Object.defineProperty(window,o,{value:a=>(r&&Reflect.deleteProperty(window,o),n?.(a)),writable:!1,configurable:!0}),i}var c,l=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0;w(this,c,()=>{});this.id=u(r=>{_(this,c).call(this,r)})}set onmessage(r){A(this,c,r)}get onmessage(){return _(this,c)}toJSON(){return`__CHANNEL__:${this.id}`}};c=new WeakMap;var g=class{constructor(r,i,o){this.plugin=r,this.event=i,this.channelId=o}async unregister(){return t(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function L(n,r,i){let o=new l;return o.onmessage=i,t(`plugin:${n}|register_listener`,{event:r,handler:o}).then(()=>new g(n,r,o.id))}async function t(n,r={},i){return new Promise((o,a)=>{let v=u(d=>{o(d),Reflect.deleteProperty(window,`_${P}`)},!0),P=u(d=>{a(d),Reflect.deleteProperty(window,`_${v}`)},!0);window.__TAURI_IPC__({cmd:n,callback:v,error:P,payload:r,options:i})})}function U(n,r="asset"){return window.__TAURI__.convertFileSrc(n,r)}var b=(s=>(s.WINDOW_RESIZED="tauri://resize",s.WINDOW_MOVED="tauri://move",s.WINDOW_CLOSE_REQUESTED="tauri://close-requested",s.WINDOW_CREATED="tauri://window-created",s.WINDOW_DESTROYED="tauri://destroyed",s.WINDOW_FOCUS="tauri://focus",s.WINDOW_BLUR="tauri://blur",s.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",s.WINDOW_THEME_CHANGED="tauri://theme-changed",s.WINDOW_FILE_DROP="tauri://file-drop",s.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",s.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",s.MENU="tauri://menu",s))(b||{});async function I(n,r){await t("plugin:event|unlisten",{event:n,eventId:r})}async function R(n,r,i){return t("plugin:event|listen",{event:n,windowLabel:i?.target,handler:u(r)}).then(o=>async()=>I(n,o))}async function F(n,r,i){return R(n,o=>{r(o),I(n,o.id).catch(()=>{})},i)}async function x(n,r,i){await t("plugin:event|emit",{event:n,windowLabel:i?.target,payload:r})}var h={};p(h,{BaseDirectory:()=>O,appCacheDir:()=>V,appConfigDir:()=>S,appDataDir:()=>H,appLocalDataDir:()=>$,appLogDir:()=>an,audioDir:()=>M,basename:()=>yn,cacheDir:()=>j,configDir:()=>z,dataDir:()=>G,delimiter:()=>pn,desktopDir:()=>q,dirname:()=>mn,documentDir:()=>J,downloadDir:()=>K,executableDir:()=>Q,extname:()=>_n,fontDir:()=>Y,homeDir:()=>Z,isAbsolute:()=>fn,join:()=>dn,localDataDir:()=>X,normalize:()=>gn,pictureDir:()=>B,publicDir:()=>nn,resolve:()=>ln,resolveResource:()=>en,resourceDir:()=>rn,runtimeDir:()=>tn,sep:()=>un,tempDir:()=>cn,templateDir:()=>on,videoDir:()=>sn});var O=(e=>(e[e.Audio=1]="Audio",e[e.Cache=2]="Cache",e[e.Config=3]="Config",e[e.Data=4]="Data",e[e.LocalData=5]="LocalData",e[e.Document=6]="Document",e[e.Download=7]="Download",e[e.Picture=8]="Picture",e[e.Public=9]="Public",e[e.Video=10]="Video",e[e.Resource=11]="Resource",e[e.Temp=12]="Temp",e[e.AppConfig=13]="AppConfig",e[e.AppData=14]="AppData",e[e.AppLocalData=15]="AppLocalData",e[e.AppCache=16]="AppCache",e[e.AppLog=17]="AppLog",e[e.Desktop=18]="Desktop",e[e.Executable=19]="Executable",e[e.Font=20]="Font",e[e.Home=21]="Home",e[e.Runtime=22]="Runtime",e[e.Template=23]="Template",e))(O||{});async function S(){return t("plugin:path|resolve_directory",{directory:13})}async function H(){return t("plugin:path|resolve_directory",{directory:14})}async function $(){return t("plugin:path|resolve_directory",{directory:15})}async function V(){return t("plugin:path|resolve_directory",{directory:16})}async function M(){return t("plugin:path|resolve_directory",{directory:1})}async function j(){return t("plugin:path|resolve_directory",{directory:2})}async function z(){return t("plugin:path|resolve_directory",{directory:3})}async function G(){return t("plugin:path|resolve_directory",{directory:4})}async function q(){return t("plugin:path|resolve_directory",{directory:18})}async function J(){return t("plugin:path|resolve_directory",{directory:6})}async function K(){return t("plugin:path|resolve_directory",{directory:7})}async function Q(){return t("plugin:path|resolve_directory",{directory:19})}async function Y(){return t("plugin:path|resolve_directory",{directory:20})}async function Z(){return t("plugin:path|resolve_directory",{directory:21})}async function X(){return t("plugin:path|resolve_directory",{directory:5})}async function B(){return t("plugin:path|resolve_directory",{directory:8})}async function nn(){return t("plugin:path|resolve_directory",{directory:9})}async function rn(){return t("plugin:path|resolve_directory",{directory:11})}async function en(n){return t("plugin:path|resolve_directory",{directory:11,path:n})}async function tn(){return t("plugin:path|resolve_directory",{directory:22})}async function on(){return t("plugin:path|resolve_directory",{directory:23})}async function sn(){return t("plugin:path|resolve_directory",{directory:10})}async function an(){return t("plugin:path|resolve_directory",{directory:17})}async function cn(n){return t("plugin:path|resolve_directory",{directory:12})}function un(){return window.__TAURI__.path.__sep}function pn(){return window.__TAURI__.path.__delimiter}async function ln(...n){return t("plugin:path|resolve",{paths:n})}async function gn(n){return t("plugin:path|normalize",{path:n})}async function dn(...n){return t("plugin:path|join",{paths:n})}async function mn(n){return t("plugin:path|dirname",{path:n})}async function _n(n){return t("plugin:path|extname",{path:n})}async function yn(n,r){return t("plugin:path|basename",{path:n,ext:r})}async function fn(n){return t("plugin:path|isAbsolute",{path:n})}var hn=t;return k(vn);})(); window.__TAURI__ = __TAURI_IIFE__ diff --git a/core/tauri/scripts/core.js b/core/tauri/scripts/core.js index 9af28ee1b610..6c33e0a0b64b 100644 --- a/core/tauri/scripts/core.js +++ b/core/tauri/scripts/core.js @@ -13,6 +13,15 @@ }) } + const osName = __TEMPLATE_os_name__ + + window.__TAURI__.convertFileSrc = function convertFileSrc(filePath, protocol = 'asset') { + const path = encodeURIComponent(filePath) + return osName === 'windows' || osName === 'android' + ? `https://${protocol}.localhost/${path}` + : `${protocol}://localhost/${path}` + } + window.__TAURI__.transformCallback = function transformCallback( callback, once @@ -48,7 +57,7 @@ } } - window.__TAURI_INVOKE__ = function invoke(cmd, args = {}) { + window.__TAURI_INVOKE__ = function invoke(cmd, payload = {}, options) { return new Promise(function (resolve, reject) { var callback = window.__TAURI__.transformCallback(function (r) { resolve(r) @@ -59,19 +68,13 @@ delete window[`_${callback}`] }, true) - if (typeof cmd === 'string') { - args.cmd = cmd - } else if (typeof cmd === 'object') { - args = cmd - } else { - return reject(new Error('Invalid argument type.')) - } - const action = () => { window.__TAURI_IPC__({ - ...args, + cmd, callback, - error: error + error, + payload, + options }) } if (window.__TAURI_IPC__) { diff --git a/core/tauri/scripts/hotkey.js b/core/tauri/scripts/hotkey.js deleted file mode 100644 index 0bf6f6d73649..000000000000 --- a/core/tauri/scripts/hotkey.js +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -/*! hotkeys-js v3.8.7 | MIT (c) 2021 kenny wong | http://jaywcjlove.github.io/hotkeys */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).hotkeys=t()}(this,function(){"use strict";var e="undefined"!=typeof navigator&&0 { + const { cmd, callback, error, payload, options } = message + + // use custom protocol for IPC if the flag is set to true, the command is the fetch data command or when not on Linux/Android + if (useCustomProtocol || cmd === fetchChannelDataCommand || (osName !== 'linux' && osName !== 'android')) { + const { contentType, data } = processIpcMessage(payload) + fetch(window.__TAURI__.convertFileSrc(cmd, 'ipc'), { + method: 'POST', + body: data, + headers: { + 'Content-Type': contentType, + 'Tauri-Callback': callback, + 'Tauri-Error': error, + ...options?.headers + } + }).then((response) => { + const cb = response.ok ? callback : error + // we need to split here because on Android the content-type gets duplicated + switch ((response.headers.get('content-type') || '').split(',')[0]) { + case 'application/json': + return response.json().then((r) => [cb, r]) + case 'text/plain': + return response.text().then((r) => [cb, r]) + default: + return response.arrayBuffer().then((r) => [cb, r]) + } + }).then(([cb, data]) => { + if (window[`_${cb}`]) { + window[`_${cb}`](data) + } else { + console.warn(`[TAURI] Couldn't find callback id {cb} in window. This might happen when the app is reloaded while Rust is running an asynchronous operation.`) + } + }) + } else { + // otherwise use the postMessage interface + const { data } = processIpcMessage({ cmd, callback, error, options, ...payload }) + window.ipc.postMessage(data) + } + } + }) +})() diff --git a/core/tauri/scripts/ipc.js b/core/tauri/scripts/ipc.js index 83ba1121d287..433eba1e5db4 100644 --- a/core/tauri/scripts/ipc.js +++ b/core/tauri/scripts/ipc.js @@ -33,11 +33,14 @@ * @return {boolean} - if the event was a valid isolation message */ function isIsolationMessage(event) { - return ( - typeof event.data === 'object' && - 'nonce' in event.data && - 'payload' in event.data - ) + if (typeof event.data === 'object' && typeof event.data.payload === 'object') { + const keys = Object.keys(event.data.payload) + return ( + keys.length > 0 && + keys.every((key) => key === 'nonce' || key === 'payload') + ) + } + return false } /** @@ -47,7 +50,12 @@ * @return {boolean} - if the data is able to transform into an isolation payload */ function isIsolationPayload(data) { - return typeof data === 'object' && 'callback' in data && 'error' in data + return ( + typeof data === 'object' && + 'callback' in data && + 'error' in data && + !isIsolationMessage(data) + ) } /** diff --git a/core/tauri/scripts/process-ipc-message-fn.js b/core/tauri/scripts/process-ipc-message-fn.js new file mode 100644 index 000000000000..2e8dade7bdad --- /dev/null +++ b/core/tauri/scripts/process-ipc-message-fn.js @@ -0,0 +1,31 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +// this is a function and not an iife so use it carefully + +(function (message) { + if (message instanceof ArrayBuffer || ArrayBuffer.isView(message) || Array.isArray(message)) { + return { + contentType: 'application/octet-stream', + data: message + } + } else { + const data = JSON.stringify(message, (_k, val) => { + if (val instanceof Map) { + let o = {}; + val.forEach((v, k) => o[k] = v); + return o; + } else if (val instanceof Object && '__TAURI_CHANNEL_MARKER__' in val && typeof val.id === 'number') { + return `__CHANNEL__:${val.id}` + } else { + return val; + } + }) + + return { + contentType: 'application/json', + data + } + } +}) diff --git a/core/tauri/scripts/stringify-ipc-message-fn.js b/core/tauri/scripts/stringify-ipc-message-fn.js deleted file mode 100644 index 5330a1358815..000000000000 --- a/core/tauri/scripts/stringify-ipc-message-fn.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2019-2023 Tauri Programme within The Commons Conservancy -// SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: MIT - -(function (message) { - return JSON.stringify(message, (_k, val) => { - if (val instanceof Map) { - let o = {}; - val.forEach((v, k) => o[k] = v); - return o; - } else if (val instanceof Object && '__TAURI_CHANNEL_MARKER__' in val && typeof val.id === 'number') { - return `__CHANNEL__:${val.id}` - } else { - return val; - } - }) -}) diff --git a/core/tauri/src/api/mod.rs b/core/tauri/src/api/mod.rs index 5dd153960190..10742b7baeb6 100644 --- a/core/tauri/src/api/mod.rs +++ b/core/tauri/src/api/mod.rs @@ -4,8 +4,6 @@ //! The Tauri API interface. -pub mod ipc; - mod error; pub use error::{Error, Result}; diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 5a60719fa1a2..a09fb9131011 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -6,10 +6,10 @@ pub(crate) mod tray; use crate::{ - api::ipc::CallbackFn, command::{CommandArg, CommandItem}, - hooks::{ - window_invoke_responder, InvokeHandler, InvokeResponder, OnPageLoad, PageLoadPayload, SetupHook, + ipc::{ + channel::ChannelDataIpcQueue, CallbackFn, Invoke, InvokeError, InvokeHandler, InvokeResponder, + InvokeResponse, }, manager::{Asset, CustomProtocol, WindowManager}, plugin::{Plugin, PluginStore}, @@ -23,14 +23,16 @@ use crate::{ sealed::{ManagerBase, RuntimeOrDispatch}, utils::config::Config, utils::{assets::Assets, Env}, - Context, DeviceEventFilter, EventLoopMessage, Icon, Invoke, InvokeError, InvokeResponse, Manager, - Monitor, Runtime, Scopes, StateManager, Theme, Window, + Context, DeviceEventFilter, EventLoopMessage, Icon, Manager, Monitor, Runtime, Scopes, + StateManager, Theme, Window, }; #[cfg(feature = "protocol-asset")] use crate::scope::FsScope; use raw_window_handle::HasRawDisplayHandle; +use serde::Deserialize; +use serialize_to_javascript::{default_template, DefaultTemplate, Template}; use tauri_macros::default_runtime; use tauri_runtime::window::{ dpi::{PhysicalPosition, PhysicalSize}, @@ -55,6 +57,24 @@ pub(crate) type GlobalMenuEventListener = Box) + Se pub(crate) type GlobalWindowEventListener = Box) + Send + Sync>; #[cfg(all(desktop, feature = "system-tray"))] type SystemTrayEventListener = Box, tray::SystemTrayEvent) + Send + Sync>; +/// A closure that is run when the Tauri application is setting up. +pub type SetupHook = + Box) -> Result<(), Box> + Send>; +/// A closure that is run once every time a window is created and loaded. +pub type OnPageLoad = dyn Fn(Window, PageLoadPayload) + Send + Sync + 'static; + +/// The payload for the [`OnPageLoad`] hook. +#[derive(Debug, Clone, Deserialize)] +pub struct PageLoadPayload { + url: String, +} + +impl PageLoadPayload { + /// The page URL. + pub fn url(&self) -> &str { + &self.url + } +} /// Api exposed on the `ExitRequested` event. #[derive(Debug)] @@ -799,7 +819,7 @@ pub struct Builder { invoke_handler: Box>, /// The JS message responder. - pub(crate) invoke_responder: Arc>, + invoke_responder: Option>>, /// The script that initializes the `window.__TAURI_POST_MESSAGE__` function. invoke_initialization_script: String, @@ -847,6 +867,17 @@ pub struct Builder { device_event_filter: DeviceEventFilter, } +#[derive(Template)] +#[default_template("../scripts/ipc-protocol.js")] +struct InvokeInitializationScript<'a> { + /// The function that processes the IPC message. + #[raw] + process_ipc_message_fn: &'a str, + os_name: &'a str, + fetch_channel_data_command: &'a str, + use_custom_protocol: bool, +} + impl Builder { /// Creates a new App builder. pub fn new() -> Self { @@ -855,9 +886,16 @@ impl Builder { runtime_any_thread: false, setup: Box::new(|_| Ok(())), invoke_handler: Box::new(|_| false), - invoke_responder: Arc::new(window_invoke_responder), - invoke_initialization_script: - format!("Object.defineProperty(window, '__TAURI_POST_MESSAGE__', {{ value: (message) => window.ipc.postMessage({}(message)) }})", crate::manager::STRINGIFY_IPC_MESSAGE_FN), + invoke_responder: None, + invoke_initialization_script: InvokeInitializationScript { + process_ipc_message_fn: crate::manager::PROCESS_IPC_MESSAGE_FN, + os_name: std::env::consts::OS, + fetch_channel_data_command: crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND, + use_custom_protocol: cfg!(ipc_custom_protocol), + } + .render_default(&Default::default()) + .unwrap() + .into_string(), on_page_load: Box::new(|_, _| ()), pending_windows: Default::default(), plugins: PluginStore::default(), @@ -916,14 +954,14 @@ impl Builder { /// The `responder` is a function that will be called when a command has been executed and must send a response to the JS layer. /// /// The `initialization_script` is a script that initializes `window.__TAURI_POST_MESSAGE__`. - /// That function must take the `message: object` argument and send it to the backend. + /// That function must take the `(message: object, options: object)` arguments and send it to the backend. #[must_use] pub fn invoke_system(mut self, initialization_script: String, responder: F) -> Self where - F: Fn(Window, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static, + F: Fn(Window, String, &InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static, { self.invoke_initialization_script = initialization_script; - self.invoke_responder = Arc::new(responder); + self.invoke_responder.replace(Arc::new(responder)); self } @@ -1368,6 +1406,9 @@ impl Builder { asset_protocol: FsScope::for_fs_api(&app, &app.config().tauri.security.asset_protocol.scope)?, }); + app.manage(ChannelDataIpcQueue::default()); + app.handle.plugin(crate::ipc::channel::plugin())?; + #[cfg(windows)] { if let crate::utils::config::WebviewInstallMode::FixedRuntime { path } = &app diff --git a/core/tauri/src/command.rs b/core/tauri/src/command.rs index 7f392d480826..f3da5359dcf5 100644 --- a/core/tauri/src/command.rs +++ b/core/tauri/src/command.rs @@ -7,11 +7,14 @@ //! You usually don't need to create these items yourself. These are created from [command](../attr.command.html) //! attribute macro along the way and used by [`crate::generate_handler`] macro. -use crate::hooks::InvokeError; -use crate::InvokeMessage; -use crate::Runtime; -use serde::de::Visitor; -use serde::{Deserialize, Deserializer}; +use crate::{ + ipc::{InvokeBody, InvokeError, InvokeMessage}, + Runtime, +}; +use serde::{ + de::{Error, Visitor}, + Deserialize, Deserializer, +}; /// Represents a custom command. pub struct CommandItem<'a, R: Runtime> { @@ -62,8 +65,6 @@ impl<'de, D: Deserialize<'de>, R: Runtime> CommandArg<'de, R> for D { macro_rules! pass { ($fn:ident, $($arg:ident: $argt:ty),+) => { fn $fn>(self, $($arg: $argt),*) -> Result { - use serde::de::Error; - if self.key.is_empty() { return Err(serde_json::Error::custom(format!( "command {} has an argument with no name with a non-optional value", @@ -71,14 +72,24 @@ macro_rules! pass { ))) } - match self.message.payload.get(self.key) { - Some(value) => value.$fn($($arg),*), - None => { + match &self.message.payload { + InvokeBody::Raw(_body) => { Err(serde_json::Error::custom(format!( - "command {} missing required key {}", + "command {} expected a value for key {} but the IPC call used a bytes payload", self.name, self.key ))) } + InvokeBody::Json(v) => { + match v.get(self.key) { + Some(value) => value.$fn($($arg),*), + None => { + Err(serde_json::Error::custom(format!( + "command {} missing required key {}", + self.name, self.key + ))) + } + } + } } } } @@ -111,9 +122,15 @@ impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> { pass!(deserialize_byte_buf, visitor: V); fn deserialize_option>(self, visitor: V) -> Result { - match self.message.payload.get(self.key) { - Some(value) => value.deserialize_option(visitor), - None => visitor.visit_none(), + match &self.message.payload { + InvokeBody::Raw(_body) => Err(serde_json::Error::custom(format!( + "command {} expected a value for key {} but the IPC call used a bytes payload", + self.name, self.key + ))), + InvokeBody::Json(v) => match v.get(self.key) { + Some(value) => value.deserialize_option(visitor), + None => visitor.visit_none(), + }, } } @@ -155,46 +172,47 @@ impl<'de, R: Runtime> Deserializer<'de> for CommandItem<'de, R> { /// Nothing in this module is considered stable. #[doc(hidden)] pub mod private { - use crate::{InvokeError, InvokeResolver, Runtime}; + use crate::{ + ipc::{InvokeBody, InvokeError, InvokeResolver, IpcResponse}, + Runtime, + }; use futures_util::{FutureExt, TryFutureExt}; - use serde::Serialize; - use serde_json::Value; use std::future::Future; - // ===== impl Serialize ===== + // ===== impl IpcResponse ===== - pub struct SerializeTag; + pub struct ResponseTag; - pub trait SerializeKind { + pub trait ResponseKind { #[inline(always)] - fn blocking_kind(&self) -> SerializeTag { - SerializeTag + fn blocking_kind(&self) -> ResponseTag { + ResponseTag } #[inline(always)] - fn async_kind(&self) -> SerializeTag { - SerializeTag + fn async_kind(&self) -> ResponseTag { + ResponseTag } } - impl SerializeKind for &T {} + impl ResponseKind for &T {} - impl SerializeTag { + impl ResponseTag { #[inline(always)] pub fn block(self, value: T, resolver: InvokeResolver) where R: Runtime, - T: Serialize, + T: IpcResponse, { resolver.respond(Ok(value)) } #[inline(always)] - pub fn future(self, value: T) -> impl Future> + pub fn future(self, value: T) -> impl Future> where - T: Serialize, + T: IpcResponse, { - std::future::ready(serde_json::to_value(value).map_err(InvokeError::from_serde_json)) + std::future::ready(value.body().map_err(InvokeError::from_error)) } } @@ -214,14 +232,14 @@ pub mod private { } } - impl> ResultKind for Result {} + impl> ResultKind for Result {} impl ResultTag { #[inline(always)] pub fn block(self, value: Result, resolver: InvokeResolver) where R: Runtime, - T: Serialize, + T: IpcResponse, E: Into, { resolver.respond(value.map_err(Into::into)) @@ -231,20 +249,20 @@ pub mod private { pub fn future( self, value: Result, - ) -> impl Future> + ) -> impl Future> where - T: Serialize, + T: IpcResponse, E: Into, { std::future::ready( value .map_err(Into::into) - .and_then(|value| serde_json::to_value(value).map_err(InvokeError::from_serde_json)), + .and_then(|value| value.body().map_err(InvokeError::from_error)), ) } } - // ===== Future ===== + // ===== Future ===== pub struct FutureTag; @@ -254,16 +272,16 @@ pub mod private { FutureTag } } - impl> FutureKind for &F {} + impl> FutureKind for &F {} impl FutureTag { #[inline(always)] - pub fn future(self, value: F) -> impl Future> + pub fn future(self, value: F) -> impl Future> where - T: Serialize, + T: IpcResponse, F: Future + Send + 'static, { - value.map(|value| serde_json::to_value(value).map_err(InvokeError::from_serde_json)) + value.map(|value| value.body().map_err(InvokeError::from_error)) } } @@ -278,19 +296,22 @@ pub mod private { } } - impl, F: Future>> ResultFutureKind for F {} + impl, F: Future>> ResultFutureKind + for F + { + } impl ResultFutureTag { #[inline(always)] - pub fn future(self, value: F) -> impl Future> + pub fn future(self, value: F) -> impl Future> where - T: Serialize, + T: IpcResponse, E: Into, F: Future> + Send, { - value.err_into().map(|result| { - result.and_then(|value| serde_json::to_value(value).map_err(InvokeError::from_serde_json)) - }) + value + .err_into() + .map(|result| result.and_then(|value| value.body().map_err(InvokeError::from_error))) } } } diff --git a/core/tauri/src/event/commands.rs b/core/tauri/src/event/commands.rs index 770911467e34..d710a84a0279 100644 --- a/core/tauri/src/event/commands.rs +++ b/core/tauri/src/event/commands.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use crate::{api::ipc::CallbackFn, command, Manager, Result, Runtime, Window}; +use crate::{command, ipc::CallbackFn, Manager, Result, Runtime, Window}; use serde::{Deserialize, Deserializer}; use serde_json::Value as JsonValue; use tauri_runtime::window::is_label_valid; diff --git a/core/tauri/src/ios.rs b/core/tauri/src/ios.rs index 5b9af9ec3d89..16bb3c6e5cf7 100644 --- a/core/tauri/src/ios.rs +++ b/core/tauri/src/ios.rs @@ -9,7 +9,7 @@ use swift_rs::{swift, SRString, SwiftArg}; use std::{ ffi::c_void, - os::raw::{c_char, c_int}, + os::raw::{c_char, c_int, c_ulonglong}, }; type PluginMessageCallbackFn = unsafe extern "C" fn(c_int, c_int, *const c_char); @@ -23,20 +23,24 @@ impl<'a> SwiftArg<'a> for PluginMessageCallback { } } -swift!(pub fn post_ipc_message( - webview: *const c_void, - name: &SRString, - method: &SRString, - data: *const c_void, - callback: usize, - error: usize -)); -swift!(pub fn run_plugin_method( +type ChannelSendDataCallbackFn = unsafe extern "C" fn(c_ulonglong, *const c_char); +pub struct ChannelSendDataCallback(pub ChannelSendDataCallbackFn); + +impl<'a> SwiftArg<'a> for ChannelSendDataCallback { + type ArgType = ChannelSendDataCallbackFn; + + unsafe fn as_arg(&'a self) -> Self::ArgType { + self.0 + } +} + +swift!(pub fn run_plugin_command( id: i32, name: &SRString, method: &SRString, data: *const c_void, - callback: PluginMessageCallback + callback: PluginMessageCallback, + send_channel_data_callback: ChannelSendDataCallback )); swift!(pub fn register_plugin( name: &SRString, @@ -108,7 +112,7 @@ unsafe fn add_json_value_to_array(array: id, value: &JsonValue) { let () = msg_send![array, addObject: number]; } JsonValue::String(val) => { - let () = msg_send![array, addObject: NSString::new(&val)]; + let () = msg_send![array, addObject: NSString::new(val)]; } JsonValue::Array(val) => { let nsarray: id = msg_send![class!(NSMutableArray), alloc]; @@ -130,7 +134,7 @@ unsafe fn add_json_value_to_array(array: id, value: &JsonValue) { } unsafe fn add_json_entry_to_dictionary(data: id, key: &str, value: &JsonValue) { - let key = NSString::new(&key); + let key = NSString::new(key); match value { JsonValue::Null => { let null: id = msg_send![class!(NSNull), null]; @@ -154,7 +158,7 @@ unsafe fn add_json_entry_to_dictionary(data: id, key: &str, value: &JsonValue) { let () = msg_send![data, setObject:number forKey: key]; } JsonValue::String(val) => { - let () = msg_send![data, setObject:NSString::new(&val) forKey: key]; + let () = msg_send![data, setObject:NSString::new(val) forKey: key]; } JsonValue::Array(val) => { let nsarray: id = msg_send![class!(NSMutableArray), alloc]; diff --git a/core/tauri/src/ipc/channel.rs b/core/tauri/src/ipc/channel.rs new file mode 100644 index 000000000000..e09a8d922b53 --- /dev/null +++ b/core/tauri/src/ipc/channel.rs @@ -0,0 +1,151 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use serde::{Deserialize, Serialize, Serializer}; + +use crate::{ + command, + command::{CommandArg, CommandItem}, + plugin::{Builder as PluginBuilder, TauriPlugin}, + Manager, Runtime, State, Window, +}; + +use super::{CallbackFn, InvokeBody, InvokeError, IpcResponse, Request, Response}; + +pub const IPC_PAYLOAD_PREFIX: &str = "__CHANNEL__:"; +pub const CHANNEL_PLUGIN_NAME: &str = "__TAURI_CHANNEL__"; +// TODO: ideally this const references CHANNEL_PLUGIN_NAME +pub const FETCH_CHANNEL_DATA_COMMAND: &str = "plugin:__TAURI_CHANNEL__|fetch"; +pub(crate) const CHANNEL_ID_HEADER_NAME: &str = "Tauri-Channel-Id"; + +/// Maps a channel id to a pending data that must be send to the JavaScript side via the IPC. +#[derive(Default, Clone)] +pub struct ChannelDataIpcQueue(pub(crate) Arc>>); + +/// An IPC channel. +#[derive(Clone)] +pub struct Channel { + id: u32, + on_message: Arc crate::Result<()> + Send + Sync>, +} + +impl Serialize for Channel { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("{IPC_PAYLOAD_PREFIX}{}", self.id)) + } +} + +impl Channel { + /// Creates a new channel with the given message handler. + pub fn new crate::Result<()> + Send + Sync + 'static>( + on_message: F, + ) -> Self { + Self::_new(rand::random(), on_message) + } + + pub(crate) fn _new crate::Result<()> + Send + Sync + 'static>( + id: u32, + on_message: F, + ) -> Self { + #[allow(clippy::let_and_return)] + let channel = Self { + id, + on_message: Arc::new(on_message), + }; + + #[cfg(mobile)] + crate::plugin::mobile::register_channel(channel.clone()); + + channel + } + + pub(crate) fn from_ipc(window: Window, callback: CallbackFn) -> Self { + Channel::_new(callback.0, move |body| { + let data_id = rand::random(); + window + .state::() + .0 + .lock() + .unwrap() + .insert(data_id, body); + window.eval(&format!( + "__TAURI_INVOKE__('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': {data_id} }} }}).then(window['_' + {}]).catch(console.error)", + callback.0 + )) + }) + } + + pub(crate) fn load_from_ipc( + window: Window, + value: impl AsRef, + ) -> Option { + value + .as_ref() + .split_once(IPC_PAYLOAD_PREFIX) + .and_then(|(_prefix, id)| id.parse().ok()) + .map(|callback_id| Self::from_ipc(window, CallbackFn(callback_id))) + } + + /// The channel identifier. + pub fn id(&self) -> u32 { + self.id + } + + /// Sends the given data through the channel. + pub fn send(&self, data: T) -> crate::Result<()> { + let body = data.body()?; + (self.on_message)(body) + } +} + +impl<'de, R: Runtime> CommandArg<'de, R> for Channel { + /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`Channel`]. + fn from_command(command: CommandItem<'de, R>) -> Result { + let name = command.name; + let arg = command.key; + let window = command.message.window(); + let value: String = + Deserialize::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e))?; + Channel::load_from_ipc(window, &value).ok_or_else(|| { + InvokeError::from_anyhow(anyhow::anyhow!( + "invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format" + )) + }) + } +} + +#[command(root = "crate")] +fn fetch( + request: Request<'_>, + cache: State<'_, ChannelDataIpcQueue>, +) -> Result { + if let Some(id) = request + .headers() + .get(CHANNEL_ID_HEADER_NAME) + .and_then(|v| v.to_str().ok()) + .and_then(|id| id.parse().ok()) + { + if let Some(data) = cache.0.lock().unwrap().remove(&id) { + Ok(Response::new(data)) + } else { + Err("data not found") + } + } else { + Err("missing channel id header") + } +} + +pub fn plugin() -> TauriPlugin { + PluginBuilder::new(CHANNEL_PLUGIN_NAME) + .invoke_handler(crate::generate_handler![fetch]) + .build() +} diff --git a/core/tauri/src/api/ipc.rs b/core/tauri/src/ipc/format_callback.rs similarity index 57% rename from core/tauri/src/api/ipc.rs rename to core/tauri/src/ipc/format_callback.rs index e4fccc99e45c..31d7cbc599d1 100644 --- a/core/tauri/src/api/ipc.rs +++ b/core/tauri/src/ipc/format_callback.rs @@ -2,82 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -//! Types and functions related to Inter Procedure Call(IPC). -//! -//! This module includes utilities to send messages to the JS layer of the webview. - -use serde::{Deserialize, Serialize}; +use serde::Serialize; use serde_json::value::RawValue; -pub use serialize_to_javascript::Options as SerializeOptions; use serialize_to_javascript::Serialized; -use tauri_macros::default_runtime; - -use crate::{ - command::{CommandArg, CommandItem}, - InvokeError, Runtime, Window, -}; - -const CHANNEL_PREFIX: &str = "__CHANNEL__:"; - -/// An IPC channel. -#[default_runtime(crate::Wry, wry)] -pub struct Channel { - id: CallbackFn, - window: Window, -} - -impl Clone for Channel { - fn clone(&self) -> Self { - Self { - id: self.id, - window: self.window.clone(), - } - } -} - -impl Serialize for Channel { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&format!("{CHANNEL_PREFIX}{}", self.id.0)) - } -} - -impl Channel { - /// Sends the given data through the channel. - pub fn send(&self, data: &S) -> crate::Result<()> { - let js = format_callback(self.id, data)?; - self.window.eval(&js) - } -} -impl<'de, R: Runtime> CommandArg<'de, R> for Channel { - /// Grabs the [`Window`] from the [`CommandItem`] and returns the associated [`Channel`]. - fn from_command(command: CommandItem<'de, R>) -> Result { - let name = command.name; - let arg = command.key; - let window = command.message.window(); - let value: String = - Deserialize::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e))?; - if let Some(callback_id) = value - .split_once(CHANNEL_PREFIX) - .and_then(|(_prefix, id)| id.parse().ok()) - { - return Ok(Channel { - id: CallbackFn(callback_id), - window, - }); - } - Err(InvokeError::from_anyhow(anyhow::anyhow!( - "invalid channel value `{value}`, expected a string in the `{CHANNEL_PREFIX}ID` format" - ))) - } -} - -/// The `Callback` type is the return value of the `transformCallback` JavaScript function. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct CallbackFn(pub usize); +use super::CallbackFn; /// The information about this is quite limited. On Chrome/Edge and Firefox, [the maximum string size is approximately 1 GB](https://stackoverflow.com/a/34958490). /// @@ -111,22 +40,9 @@ const MIN_JSON_PARSE_LEN: usize = 10_240; /// 1. `serde_json`'s ability to correctly escape and format json into a string. /// 2. JavaScript engines not accepting anything except another unescaped, literal single quote /// character to end a string that was opened with it. -/// -/// # Examples -/// -/// ``` -/// use tauri::api::ipc::{serialize_js_with, SerializeOptions}; -/// #[derive(serde::Serialize)] -/// struct Foo { -/// bar: String, -/// } -/// let foo = Foo { bar: "x".repeat(20_000).into() }; -/// let value = serialize_js_with(&foo, SerializeOptions::default(), |v| format!("console.log({v})")).unwrap(); -/// assert_eq!(value, format!("console.log(JSON.parse('{{\"bar\":\"{}\"}}'))", foo.bar)); -/// ``` -pub fn serialize_js_with String>( +fn serialize_js_with String>( value: &T, - options: SerializeOptions, + options: serialize_to_javascript::Options, cb: F, ) -> crate::api::Result { // get a raw &str representation of a serialized json value. @@ -161,80 +77,13 @@ pub fn serialize_js_with String>( Ok(return_val) } -/// Transforms & escapes a JSON value. -/// -/// This is a convenience function for [`serialize_js_with`], simply allocating the result to a String. -/// -/// For usage in functions where performance is more important than code readability, see [`serialize_js_with`]. -/// -/// # Examples -/// ```rust,no_run -/// use tauri::{Manager, api::ipc::serialize_js}; -/// use serde::Serialize; -/// -/// #[derive(Serialize)] -/// struct Foo { -/// bar: String, -/// } -/// -/// #[derive(Serialize)] -/// struct Bar { -/// baz: u32, -/// } -/// -/// tauri::Builder::default() -/// .setup(|app| { -/// let window = app.get_window("main").unwrap(); -/// window.eval(&format!( -/// "console.log({}, {})", -/// serialize_js(&Foo { bar: "bar".to_string() }).unwrap(), -/// serialize_js(&Bar { baz: 0 }).unwrap()), -/// )?; -/// Ok(()) -/// }); -/// ``` -pub fn serialize_js(value: &T) -> crate::api::Result { - serialize_js_with(value, Default::default(), |v| v.into()) -} - /// Formats a function name and argument to be evaluated as callback. /// /// This will serialize primitive JSON types (e.g. booleans, strings, numbers, etc.) as JavaScript literals, /// but will serialize arrays and objects whose serialized JSON string is smaller than 1 GB and larger /// than 10 KiB with `JSON.parse('...')`. /// See [json-parse-benchmark](https://github.com/GoogleChromeLabs/json-parse-benchmark). -/// -/// # Examples -/// - With string literals: -/// ``` -/// use tauri::api::ipc::{CallbackFn, format_callback}; -/// // callback with a string argument -/// let cb = format_callback(CallbackFn(12345), &"the string response").unwrap(); -/// assert!(cb.contains(r#"window["_12345"]("the string response")"#)); -/// ``` -/// -/// - With types implement [`serde::Serialize`]: -/// ``` -/// use tauri::api::ipc::{CallbackFn, format_callback}; -/// use serde::Serialize; -/// -/// // callback with large JSON argument -/// #[derive(Serialize)] -/// struct MyResponse { -/// value: String -/// } -/// -/// let cb = format_callback( -/// CallbackFn(6789), -/// &MyResponse { value: String::from_utf8(vec![b'X'; 10_240]).unwrap() -/// }).expect("failed to serialize"); -/// -/// assert!(cb.contains(r#"window["_6789"](JSON.parse('{"value":"XXXXXXXXX"#)); -/// ``` -pub fn format_callback( - function_name: CallbackFn, - arg: &T, -) -> crate::api::Result { +pub fn format(function_name: CallbackFn, arg: &T) -> crate::api::Result { serialize_js_with(arg, Default::default(), |arg| { format!( r#" @@ -258,42 +107,33 @@ pub fn format_callback( /// * `error_callback` the function name of the Err callback. Usually the `reject` of the JS Promise. /// /// Note that the callback strings are automatically generated by the `invoke` helper. -/// -/// # Examples -/// ``` -/// use tauri::api::ipc::{CallbackFn, format_callback_result}; -/// let res: Result = Ok(5); -/// let cb = format_callback_result(res, CallbackFn(145), CallbackFn(0)).expect("failed to format"); -/// assert!(cb.contains(r#"window["_145"](5)"#)); -/// -/// let res: Result<&str, &str> = Err("error message here"); -/// let cb = format_callback_result(res, CallbackFn(2), CallbackFn(1)).expect("failed to format"); -/// assert!(cb.contains(r#"window["_1"]("error message here")"#)); -/// ``` -// TODO: better example to explain -pub fn format_callback_result( +pub fn format_result( result: Result, success_callback: CallbackFn, error_callback: CallbackFn, ) -> crate::api::Result { match result { - Ok(res) => format_callback(success_callback, &res), - Err(err) => format_callback(error_callback, &err), + Ok(res) => format(success_callback, &res), + Err(err) => format(error_callback, &err), } } #[cfg(test)] mod test { - use crate::api::ipc::*; + use super::*; use quickcheck::{Arbitrary, Gen}; use quickcheck_macros::quickcheck; impl Arbitrary for CallbackFn { fn arbitrary(g: &mut Gen) -> CallbackFn { - CallbackFn(usize::arbitrary(g)) + CallbackFn(u32::arbitrary(g)) } } + fn serialize_js(value: &T) -> crate::api::Result { + serialize_js_with(value, Default::default(), |v| v.into()) + } + #[test] fn test_serialize_js() { assert_eq!(serialize_js(&()).unwrap(), "null"); @@ -346,7 +186,7 @@ mod test { #[quickcheck] fn qc_formatting(f: CallbackFn, a: String) -> bool { // call format callback - let fc = format_callback(f, &a).unwrap(); + let fc = format(f, &a).unwrap(); fc.contains(&format!( r#"window["_{}"](JSON.parse('{}'))"#, f.0, @@ -358,11 +198,10 @@ mod test { )) } - // check arbitrary strings in format_callback_result + // check arbitrary strings in format_result #[quickcheck] fn qc_format_res(result: Result, c: CallbackFn, ec: CallbackFn) -> bool { - let resp = - format_callback_result(result.clone(), c, ec).expect("failed to format callback result"); + let resp = format_result(result.clone(), c, ec).expect("failed to format callback result"); let (function, value) = match result { Ok(v) => (c, v), Err(e) => (ec, e), diff --git a/core/tauri/src/hooks.rs b/core/tauri/src/ipc/mod.rs similarity index 50% rename from core/tauri/src/hooks.rs rename to core/tauri/src/ipc/mod.rs index 4bbe77f0a9d6..c55db11c2f0a 100644 --- a/core/tauri/src/hooks.rs +++ b/core/tauri/src/ipc/mod.rs @@ -2,77 +2,156 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +//! Types and functions related to Inter Procedure Call(IPC). +//! +//! This module includes utilities to send messages to the JS layer of the webview. + +use std::sync::Arc; + +use futures_util::Future; +use http::HeaderMap; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde_json::Value as JsonValue; +pub use serialize_to_javascript::Options as SerializeOptions; +use tauri_macros::default_runtime; + use crate::{ - api::ipc::{format_callback, format_callback_result, CallbackFn}, - app::App, + command::{CommandArg, CommandItem}, Runtime, StateManager, Window, }; -use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; -use serialize_to_javascript::{default_template, Template}; -use std::{future::Future, sync::Arc}; -use tauri_macros::default_runtime; +pub(crate) mod channel; +#[cfg(not(ipc_custom_protocol))] +pub(crate) mod format_callback; +pub(crate) mod protocol; -/// A closure that is run when the Tauri application is setting up. -pub type SetupHook = - Box) -> Result<(), Box> + Send>; +pub use channel::Channel; /// A closure that is run every time Tauri receives a message it doesn't explicitly handle. pub type InvokeHandler = dyn Fn(Invoke) -> bool + Send + Sync + 'static; /// A closure that is responsible for respond a JS message. pub type InvokeResponder = - dyn Fn(Window, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static; + dyn Fn(Window, String, &InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static; +type OwnedInvokeResponder = + dyn Fn(Window, String, InvokeResponse, CallbackFn, CallbackFn) + Send + Sync + 'static; + +/// Possible values of an IPC payload. +#[derive(Debug, Clone)] +pub enum InvokeBody { + /// Json payload. + Json(JsonValue), + /// Bytes payload. + Raw(Vec), +} + +impl Default for InvokeBody { + fn default() -> Self { + Self::Json(Default::default()) + } +} + +impl From for InvokeBody { + fn from(value: JsonValue) -> Self { + Self::Json(value) + } +} + +impl From> for InvokeBody { + fn from(value: Vec) -> Self { + Self::Raw(value) + } +} + +impl IpcResponse for InvokeBody { + fn body(self) -> crate::Result { + Ok(self) + } +} -/// A closure that is run once every time a window is created and loaded. -pub type OnPageLoad = dyn Fn(Window, PageLoadPayload) + Send + Sync + 'static; +impl InvokeBody { + #[allow(dead_code)] + pub(crate) fn into_json(self) -> JsonValue { + match self { + Self::Json(v) => v, + Self::Raw(v) => { + JsonValue::Array(v.into_iter().map(|n| JsonValue::Number(n.into())).collect()) + } + } + } -// todo: why is this derive broken but the output works manually? -#[derive(Template)] -#[default_template("../scripts/ipc.js")] -pub(crate) struct IpcJavascript<'a> { - pub(crate) isolation_origin: &'a str, + /// Attempts to deserialize the invoke body. + pub fn deserialize(self) -> serde_json::Result { + match self { + InvokeBody::Json(v) => serde_json::from_value(v), + InvokeBody::Raw(v) => serde_json::from_slice(&v), + } + } } -#[cfg(feature = "isolation")] -#[derive(Template)] -#[default_template("../scripts/isolation.js")] -pub(crate) struct IsolationJavascript<'a> { - pub(crate) isolation_src: &'a str, - pub(crate) style: &'a str, +/// The IPC request. +#[derive(Debug)] +pub struct Request<'a> { + body: &'a InvokeBody, + headers: &'a HeaderMap, } -/// The payload for the [`OnPageLoad`] hook. -#[derive(Debug, Clone, Deserialize)] -pub struct PageLoadPayload { - url: String, +impl<'a> Request<'a> { + /// The request body. + pub fn body(&self) -> &InvokeBody { + self.body + } + + /// Thr request headers. + pub fn headers(&self) -> &HeaderMap { + self.headers + } } -impl PageLoadPayload { - /// The page URL. - pub fn url(&self) -> &str { - &self.url +impl<'a, R: Runtime> CommandArg<'a, R> for Request<'a> { + /// Returns the invoke [`Request`]. + fn from_command(command: CommandItem<'a, R>) -> Result { + Ok(Self { + body: command.message.payload(), + headers: command.message.headers(), + }) } } -/// The payload used on the IPC invoke. -#[derive(Debug, Deserialize)] -pub struct InvokePayload { - /// The invoke command. - pub cmd: String, - /// The success callback. - pub callback: CallbackFn, - /// The error callback. - pub error: CallbackFn, - /// The payload of the message. - #[serde(flatten)] - pub inner: JsonValue, +/// Marks a type as a response to an IPC call. +pub trait IpcResponse { + /// Resolve the IPC response body. + fn body(self) -> crate::Result; +} + +impl IpcResponse for T { + fn body(self) -> crate::Result { + serde_json::to_value(self) + .map(Into::into) + .map_err(Into::into) + } +} + +/// The IPC request. +pub struct Response { + body: InvokeBody, +} + +impl IpcResponse for Response { + fn body(self) -> crate::Result { + Ok(self.body) + } +} + +impl Response { + /// Defines a response with the given body. + pub fn new(body: impl Into) -> Self { + Self { body: body.into() } + } } /// The message and resolver given to a custom command. #[default_runtime(crate::Wry, wry)] -#[derive(Debug)] pub struct Invoke { /// The message passed. pub message: InvokeMessage, @@ -83,12 +162,12 @@ pub struct Invoke { /// Error response from an [`InvokeMessage`]. #[derive(Debug)] -pub struct InvokeError(JsonValue); +pub struct InvokeError(pub JsonValue); impl InvokeError { - /// Create an [`InvokeError`] as a string of the [`serde_json::Error`] message. + /// Create an [`InvokeError`] as a string of the [`std::error::Error`] message. #[inline(always)] - pub fn from_serde_json(error: serde_json::Error) -> Self { + pub fn from_error(error: E) -> Self { Self(JsonValue::String(error.to_string())) } @@ -104,7 +183,7 @@ impl From for InvokeError { fn from(value: T) -> Self { serde_json::to_value(value) .map(Self) - .unwrap_or_else(Self::from_serde_json) + .unwrap_or_else(Self::from_error) } } @@ -119,31 +198,20 @@ impl From for InvokeError { #[derive(Debug)] pub enum InvokeResponse { /// Resolve the promise. - Ok(JsonValue), + Ok(InvokeBody), /// Reject the promise. Err(InvokeError), } -impl InvokeResponse { - /// Turn a [`InvokeResponse`] back into a serializable result. - #[inline(always)] - pub fn into_result(self) -> Result { - match self { - Self::Ok(v) => Ok(v), - Self::Err(e) => Err(e.0), - } - } -} - -impl From> for InvokeResponse { +impl> From> for InvokeResponse { #[inline] - fn from(result: Result) -> Self { + fn from(result: Result) -> Self { match result { - Ok(ok) => match serde_json::to_value(ok) { + Ok(ok) => match ok.body() { Ok(value) => Self::Ok(value), - Err(err) => Self::Err(InvokeError::from_serde_json(err)), + Err(err) => Self::Err(InvokeError::from_error(err)), }, - Err(err) => Self::Err(err), + Err(err) => Self::Err(err.into()), } } } @@ -156,9 +224,10 @@ impl From for InvokeResponse { /// Resolver of a invoke message. #[default_runtime(crate::Wry, wry)] -#[derive(Debug)] pub struct InvokeResolver { window: Window, + responder: Arc>, + cmd: String, pub(crate) callback: CallbackFn, pub(crate) error: CallbackFn, } @@ -167,6 +236,8 @@ impl Clone for InvokeResolver { fn clone(&self) -> Self { Self { window: self.window.clone(), + responder: self.responder.clone(), + cmd: self.cmd.clone(), callback: self.callback, error: self.error, } @@ -174,9 +245,17 @@ impl Clone for InvokeResolver { } impl InvokeResolver { - pub(crate) fn new(window: Window, callback: CallbackFn, error: CallbackFn) -> Self { + pub(crate) fn new( + window: Window, + responder: Arc>, + cmd: String, + callback: CallbackFn, + error: CallbackFn, + ) -> Self { Self { window, + responder, + cmd, callback, error, } @@ -185,43 +264,67 @@ impl InvokeResolver { /// Reply to the invoke promise with an async task. pub fn respond_async(self, task: F) where - T: Serialize, + T: IpcResponse, F: Future> + Send + 'static, { crate::async_runtime::spawn(async move { - Self::return_task(self.window, task, self.callback, self.error).await; + Self::return_task( + self.window, + self.responder, + task, + self.cmd, + self.callback, + self.error, + ) + .await; }); } /// Reply to the invoke promise with an async task which is already serialized. pub fn respond_async_serialized(self, task: F) where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { crate::async_runtime::spawn(async move { let response = match task.await { Ok(ok) => InvokeResponse::Ok(ok), Err(err) => InvokeResponse::Err(err), }; - Self::return_result(self.window, response, self.callback, self.error) + Self::return_result( + self.window, + self.responder, + response, + self.cmd, + self.callback, + self.error, + ) }); } /// Reply to the invoke promise with a serializable value. - pub fn respond(self, value: Result) { - Self::return_result(self.window, value.into(), self.callback, self.error) + pub fn respond(self, value: Result) { + Self::return_result( + self.window, + self.responder, + value.into(), + self.cmd, + self.callback, + self.error, + ) } /// Resolve the invoke promise with a value. - pub fn resolve(self, value: T) { - Self::return_result(self.window, Ok(value).into(), self.callback, self.error) + pub fn resolve(self, value: T) { + self.respond(Ok(value)) } /// Reject the invoke promise with a value. pub fn reject(self, value: T) { Self::return_result( self.window, - Result::<(), _>::Err(value.into()).into(), + self.responder, + Result::<(), _>::Err(value).into(), + self.cmd, self.callback, self.error, ) @@ -229,7 +332,14 @@ impl InvokeResolver { /// Reject the invoke promise with an [`InvokeError`]. pub fn invoke_error(self, error: InvokeError) { - Self::return_result(self.window, error.into(), self.callback, self.error) + Self::return_result( + self.window, + self.responder, + error.into(), + self.cmd, + self.callback, + self.error, + ) } /// Asynchronously executes the given task @@ -239,52 +349,56 @@ impl InvokeResolver { /// If the Result `is_err()`, the callback will be the `error_callback` function name and the argument will be the Err value. pub async fn return_task( window: Window, + responder: Arc>, task: F, + cmd: String, success_callback: CallbackFn, error_callback: CallbackFn, ) where - T: Serialize, + T: IpcResponse, F: Future> + Send + 'static, { let result = task.await; - Self::return_closure(window, || result, success_callback, error_callback) + Self::return_closure( + window, + responder, + || result, + cmd, + success_callback, + error_callback, + ) } - pub(crate) fn return_closure Result>( + pub(crate) fn return_closure Result>( window: Window, + responder: Arc>, f: F, + cmd: String, success_callback: CallbackFn, error_callback: CallbackFn, ) { - Self::return_result(window, f().into(), success_callback, error_callback) + Self::return_result( + window, + responder, + f().into(), + cmd, + success_callback, + error_callback, + ) } pub(crate) fn return_result( window: Window, + responder: Arc>, response: InvokeResponse, + cmd: String, success_callback: CallbackFn, error_callback: CallbackFn, ) { - (window.invoke_responder())(window, response, success_callback, error_callback); + (responder)(window, cmd, response, success_callback, error_callback); } } -pub fn window_invoke_responder( - window: Window, - response: InvokeResponse, - success_callback: CallbackFn, - error_callback: CallbackFn, -) { - let callback_string = - match format_callback_result(response.into_result(), success_callback, error_callback) { - Ok(callback_string) => callback_string, - Err(e) => format_callback(error_callback, &e.to_string()) - .expect("unable to serialize response string to json"), - }; - - let _ = window.eval(&callback_string); -} - /// An invoke message. #[default_runtime(crate::Wry, wry)] #[derive(Debug)] @@ -296,7 +410,9 @@ pub struct InvokeMessage { /// The IPC command. pub(crate) command: String, /// The JSON argument passed on the invoke message. - pub(crate) payload: JsonValue, + pub(crate) payload: InvokeBody, + /// The request headers. + pub(crate) headers: HeaderMap, } impl Clone for InvokeMessage { @@ -306,6 +422,7 @@ impl Clone for InvokeMessage { state: self.state.clone(), command: self.command.clone(), payload: self.payload.clone(), + headers: self.headers.clone(), } } } @@ -316,13 +433,15 @@ impl InvokeMessage { window: Window, state: Arc, command: String, - payload: JsonValue, + payload: InvokeBody, + headers: HeaderMap, ) -> Self { Self { window, state, command, payload, + headers, } } @@ -346,7 +465,7 @@ impl InvokeMessage { /// A reference to the payload the invoke received. #[inline(always)] - pub fn payload(&self) -> &JsonValue { + pub fn payload(&self) -> &InvokeBody { &self.payload } @@ -361,4 +480,14 @@ impl InvokeMessage { pub fn state_ref(&self) -> &StateManager { &self.state } + + /// The request headers. + #[inline(always)] + pub fn headers(&self) -> &HeaderMap { + &self.headers + } } + +/// The `Callback` type is the return value of the `transformCallback` JavaScript function. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] +pub struct CallbackFn(pub u32); diff --git a/core/tauri/src/ipc/protocol.rs b/core/tauri/src/ipc/protocol.rs new file mode 100644 index 000000000000..60883c5c88e2 --- /dev/null +++ b/core/tauri/src/ipc/protocol.rs @@ -0,0 +1,275 @@ +// Copyright 2019-2023 Tauri Programme within The Commons Conservancy +// SPDX-License-Identifier: Apache-2.0 +// SPDX-License-Identifier: MIT + +use http::{ + header::{ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_ORIGIN}, + HeaderValue, Method, StatusCode, +}; + +use crate::{ + manager::WindowManager, + runtime::http::{Request as HttpRequest, Response as HttpResponse}, + window::{InvokeRequest, UriSchemeProtocolHandler}, + Runtime, +}; + +use super::{CallbackFn, InvokeBody, InvokeResponse}; + +const TAURI_CALLBACK_HEADER_NAME: &str = "Tauri-Callback"; +const TAURI_ERROR_HEADER_NAME: &str = "Tauri-Error"; + +#[cfg(not(ipc_custom_protocol))] +pub fn message_handler( + manager: WindowManager, +) -> crate::runtime::webview::WebviewIpcHandler { + Box::new(move |window, request| handle_ipc_message(request, &manager, &window.label)) +} + +pub fn get(manager: WindowManager, label: String) -> UriSchemeProtocolHandler { + Box::new(move |request| { + let mut response = match *request.method() { + Method::POST => { + let (mut response, content_type) = match handle_ipc_request(request, &manager, &label) { + Ok(data) => match data { + InvokeResponse::Ok(InvokeBody::Json(v)) => ( + HttpResponse::new(serde_json::to_vec(&v)?.into()), + mime::APPLICATION_JSON, + ), + InvokeResponse::Ok(InvokeBody::Raw(v)) => { + (HttpResponse::new(v.into()), mime::APPLICATION_OCTET_STREAM) + } + InvokeResponse::Err(e) => { + let mut response = HttpResponse::new(serde_json::to_vec(&e.0)?.into()); + response.set_status(StatusCode::BAD_REQUEST); + (response, mime::TEXT_PLAIN) + } + }, + Err(e) => { + let mut response = HttpResponse::new(e.as_bytes().to_vec().into()); + response.set_status(StatusCode::BAD_REQUEST); + (response, mime::TEXT_PLAIN) + } + }; + + response.set_mimetype(Some(content_type.essence_str().into())); + + response + } + + Method::OPTIONS => { + let mut r = HttpResponse::new(Vec::new().into()); + r.headers_mut().insert( + ACCESS_CONTROL_ALLOW_HEADERS, + HeaderValue::from_static("Content-Type, Tauri-Callback, Tauri-Error, Tauri-Channel-Id"), + ); + r + } + + _ => { + let mut r = HttpResponse::new( + "only POST and OPTIONS are allowed" + .as_bytes() + .to_vec() + .into(), + ); + r.set_status(StatusCode::METHOD_NOT_ALLOWED); + r.set_mimetype(Some(mime::TEXT_PLAIN.essence_str().into())); + r + } + }; + + response + .headers_mut() + .insert(ACCESS_CONTROL_ALLOW_ORIGIN, HeaderValue::from_static("*")); + + Ok(response) + }) +} + +#[cfg(not(ipc_custom_protocol))] +fn handle_ipc_message(message: String, manager: &WindowManager, label: &str) { + if let Some(window) = manager.get_window(label) { + use serde::{Deserialize, Deserializer}; + + pub(crate) struct HeaderMap(http::HeaderMap); + + impl<'de> Deserialize<'de> for HeaderMap { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let map = std::collections::HashMap::::deserialize(deserializer)?; + let mut headers = http::HeaderMap::default(); + for (key, value) in map { + if let (Ok(key), Ok(value)) = ( + http::HeaderName::from_bytes(key.as_bytes()), + http::HeaderValue::from_str(&value), + ) { + headers.insert(key, value); + } else { + return Err(serde::de::Error::custom(format!( + "invalid header `{key}` `{value}`" + ))); + } + } + Ok(Self(headers)) + } + } + + #[derive(Deserialize)] + struct RequestOptions { + headers: HeaderMap, + } + + #[derive(Deserialize)] + struct Message { + cmd: String, + callback: CallbackFn, + error: CallbackFn, + #[serde(flatten)] + payload: serde_json::Value, + options: Option, + } + + #[allow(unused_mut)] + let mut invoke_message: Option> = None; + + #[cfg(feature = "isolation")] + { + #[derive(serde::Deserialize)] + struct IsolationMessage<'a> { + cmd: String, + callback: CallbackFn, + error: CallbackFn, + #[serde(flatten)] + payload: crate::utils::pattern::isolation::RawIsolationPayload<'a>, + options: Option, + } + + if let crate::Pattern::Isolation { crypto_keys, .. } = manager.pattern() { + invoke_message.replace( + serde_json::from_str::>(&message) + .map_err(Into::into) + .and_then(|message| { + Ok(Message { + cmd: message.cmd, + callback: message.callback, + error: message.error, + payload: serde_json::from_slice(&crypto_keys.decrypt(message.payload)?)?, + options: message.options, + }) + }), + ); + } + } + + match invoke_message + .unwrap_or_else(|| serde_json::from_str::(&message).map_err(Into::into)) + { + Ok(message) => { + let _ = window.on_message(InvokeRequest { + cmd: message.cmd, + callback: message.callback, + error: message.error, + body: message.payload.into(), + headers: message.options.map(|o| o.headers.0).unwrap_or_default(), + }); + } + Err(e) => { + let _ = window.eval(&format!( + r#"console.error({})"#, + serde_json::Value::String(e.to_string()) + )); + } + } + } +} + +fn handle_ipc_request( + request: &HttpRequest, + manager: &WindowManager, + label: &str, +) -> std::result::Result { + if let Some(window) = manager.get_window(label) { + // TODO: consume instead + #[allow(unused_mut)] + let mut body = request.body().clone(); + + let cmd = request + .uri() + .strip_prefix("ipc://localhost/") + .map(|c| c.to_string()) + // the `strip_prefix` only returns None when a request is made to `https://tauri.$P` on Windows + // where `$P` is not `localhost/*` + // in this case the IPC call is considered invalid + .unwrap_or_else(|| "".to_string()); + let cmd = percent_encoding::percent_decode(cmd.as_bytes()) + .decode_utf8_lossy() + .to_string(); + + // the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it + #[cfg(all(feature = "isolation", ipc_custom_protocol))] + if let crate::Pattern::Isolation { crypto_keys, .. } = manager.pattern() { + body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body) + .and_then(|raw| crypto_keys.decrypt(raw)) + .map_err(|e| e.to_string())?; + } + + let callback = CallbackFn( + request + .headers() + .get(TAURI_CALLBACK_HEADER_NAME) + .ok_or("missing Tauri-Callback header")? + .to_str() + .map_err(|_| "Tauri callback header value must be a string")? + .parse() + .map_err(|_| "Tauri callback header value must be a numeric string")?, + ); + let error = CallbackFn( + request + .headers() + .get(TAURI_ERROR_HEADER_NAME) + .ok_or("missing Tauri-Error header")? + .to_str() + .map_err(|_| "Tauri error header value must be a string")? + .parse() + .map_err(|_| "Tauri error header value must be a numeric string")?, + ); + + let content_type = request + .headers() + .get(reqwest::header::CONTENT_TYPE) + .and_then(|h| h.to_str().ok()) + .map(|mime| mime.parse()) + .unwrap_or(Ok(mime::APPLICATION_OCTET_STREAM)) + .map_err(|_| "unknown content type")?; + let body = if content_type == mime::APPLICATION_OCTET_STREAM { + body.into() + } else if content_type == mime::APPLICATION_JSON { + if cfg!(ipc_custom_protocol) { + serde_json::from_slice::(&body) + .map_err(|e| e.to_string())? + .into() + } else { + // the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it + serde_json::Value::Object(Default::default()).into() + } + } else { + return Err(format!("content type {content_type} is not implemented")); + }; + + let payload = InvokeRequest { + cmd, + callback, + error, + body, + headers: request.headers().clone(), + }; + + let rx = window.on_message(payload); + Ok(rx.recv().unwrap()) + } else { + Err("window not found".into()) + } +} diff --git a/core/tauri/src/jni_helpers.rs b/core/tauri/src/jni_helpers.rs index e6edb281077d..ae500fc5265a 100644 --- a/core/tauri/src/jni_helpers.rs +++ b/core/tauri/src/jni_helpers.rs @@ -31,7 +31,7 @@ fn json_to_java<'a, R: Runtime>( } JsonValue::String(val) => ( "Ljava/lang/Object;", - JObject::from(env.new_string(&val)?).into(), + JObject::from(env.new_string(val)?).into(), ), JsonValue::Array(val) => { let js_array_class = runtime_handle.find_class(env, activity, "app/tauri/plugin/JSArray")?; @@ -60,7 +60,7 @@ fn json_to_java<'a, R: Runtime>( data, "put", format!("(Ljava/lang/String;{signature})Lapp/tauri/plugin/JSObject;"), - &[env.new_string(&key)?.into(), val], + &[env.new_string(key)?.into(), val], )?; } diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 08668ceb341e..861f96c5fe26 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -16,7 +16,7 @@ //! - **test**: Enables the [`test`] module exposing unit test helpers. //! - **dox**: Internal feature to generate Rust documentation without linking on Linux. //! - **objc-exception**: Wrap each msg_send! in a @try/@catch and panics if an exception is caught, preventing Objective-C from unwinding into Rust. -//! - **linux-protocol-headers**: Enables headers support for custom protocol requests on Linux. Requires webkit2gtk v2.36 or above. +//! - **linux-ipc-protocol**: Use custom protocol for faster IPC on Linux. Requires webkit2gtk v2.40 or above. //! - **isolation**: Enables the isolation pattern. Enabled by default if the `tauri > pattern > use` config option is set to `isolation` on the `tauri.conf.json` file. //! - **custom-protocol**: Feature managed by the Tauri CLI. When enabled, Tauri assumes a production environment instead of a development one. //! - **devtools**: Enables the developer tools (Web inspector) and [`Window::open_devtools`]. Enabled by default on debug builds. @@ -81,7 +81,7 @@ pub mod async_runtime; pub mod command; mod error; mod event; -mod hooks; +pub mod ipc; mod manager; mod pattern; pub mod plugin; @@ -120,23 +120,31 @@ macro_rules! android_binding { handlePluginResponse, [i32, JString, JString], ); + ::tauri::wry::application::android_fn!( + app_tauri, + plugin, + PluginManager, + sendChannelData, + [i64, JString], + ); + // this function is a glue between PluginManager.kt > handlePluginResponse and Rust #[allow(non_snake_case)] - pub unsafe fn handlePluginResponse( - env: JNIEnv, - _: JClass, - id: i32, - success: JString, - error: JString, - ) { + pub fn handlePluginResponse(env: JNIEnv, _: JClass, id: i32, success: JString, error: JString) { ::tauri::handle_android_plugin_response(env, id, success, error); } + + // this function is a glue between PluginManager.kt > sendChannelData and Rust + #[allow(non_snake_case)] + pub fn sendChannelData(env: JNIEnv, _: JClass, id: i64, data: JString) { + ::tauri::send_channel_data(env, id, data); + } }; } #[cfg(all(feature = "wry", target_os = "android"))] #[doc(hidden)] -pub use plugin::mobile::handle_android_plugin_response; +pub use plugin::mobile::{handle_android_plugin_response, send_channel_data}; #[cfg(all(feature = "wry", target_os = "android"))] #[doc(hidden)] pub use tauri_runtime_wry::wry; @@ -180,10 +188,6 @@ pub use { App, AppHandle, AssetResolver, Builder, CloseRequestApi, GlobalWindowEvent, RunEvent, WindowEvent, }, - self::hooks::{ - Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokePayload, InvokeResolver, - InvokeResponder, InvokeResponse, OnPageLoad, PageLoadPayload, SetupHook, - }, self::manager::Asset, self::runtime::{ webview::WebviewAttributes, diff --git a/core/tauri/src/manager.rs b/core/tauri/src/manager.rs index 752eed9c62d5..fc45662c133e 100644 --- a/core/tauri/src/manager.rs +++ b/core/tauri/src/manager.rs @@ -11,36 +11,32 @@ use std::{ }; use serde::Serialize; -use serde_json::Value as JsonValue; use serialize_to_javascript::{default_template, DefaultTemplate, Template}; use url::Url; use tauri_macros::default_runtime; use tauri_utils::debug_eprintln; -#[cfg(feature = "isolation")] -use tauri_utils::pattern::isolation::RawIsolationPayload; use tauri_utils::{ assets::{AssetKey, CspHash}, config::{Csp, CspDirectiveSources}, html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN}, }; -use crate::app::{GlobalMenuEventListener, WindowMenuEvent}; -use crate::hooks::IpcJavascript; -#[cfg(feature = "isolation")] -use crate::hooks::IsolationJavascript; -use crate::pattern::PatternJavascript; use crate::{ - app::{AppHandle, GlobalWindowEvent, GlobalWindowEventListener}, + app::{ + AppHandle, GlobalMenuEventListener, GlobalWindowEvent, GlobalWindowEventListener, OnPageLoad, + PageLoadPayload, WindowMenuEvent, + }, event::{assert_event_name_is_valid, Event, EventHandler, Listeners}, - hooks::{InvokeHandler, InvokePayload, InvokeResponder, OnPageLoad, PageLoadPayload}, + ipc::{Invoke, InvokeHandler, InvokeResponder}, + pattern::PatternJavascript, plugin::PluginStore, runtime::{ http::{ MimeType, Request as HttpRequest, Response as HttpResponse, ResponseBuilder as HttpResponseBuilder, }, - webview::{WebviewIpcHandler, WindowBuilder}, + webview::WindowBuilder, window::{dpi::PhysicalSize, DetachedWindow, FileDropEvent, PendingWindow}, }, utils::{ @@ -48,7 +44,8 @@ use crate::{ config::{AppUrl, Config, WindowUrl}, PackageInfo, }, - Context, EventLoopMessage, Icon, Invoke, Manager, Pattern, Runtime, Scopes, StateManager, Window, + window::{UriSchemeProtocolHandler, WebResourceRequestHandler}, + Context, EventLoopMessage, Icon, Manager, Pattern, Runtime, Scopes, StateManager, Window, WindowEvent, }; @@ -70,8 +67,8 @@ const WINDOW_FILE_DROP_HOVER_EVENT: &str = "tauri://file-drop-hover"; const WINDOW_FILE_DROP_CANCELLED_EVENT: &str = "tauri://file-drop-cancelled"; const MENU_EVENT: &str = "tauri://menu"; -pub(crate) const STRINGIFY_IPC_MESSAGE_FN: &str = - include_str!("../scripts/stringify-ipc-message-fn.js"); +pub(crate) const PROCESS_IPC_MESSAGE_FN: &str = + include_str!("../scripts/process-ipc-message-fn.js"); // we need to proxy the dev server on mobile because we can't use `localhost`, so we use the local IP address // and we do not get a secure context without the custom protocol that proxies to the dev server @@ -79,6 +76,20 @@ pub(crate) const STRINGIFY_IPC_MESSAGE_FN: &str = // must also keep in sync with the `let mut response` assignment in prepare_uri_scheme_protocol const PROXY_DEV_SERVER: bool = cfg!(all(dev, mobile)); +#[cfg(feature = "isolation")] +#[derive(Template)] +#[default_template("../scripts/isolation.js")] +pub(crate) struct IsolationJavascript<'a> { + pub(crate) isolation_src: &'a str, + pub(crate) style: &'a str, +} + +#[derive(Template)] +#[default_template("../scripts/ipc.js")] +pub(crate) struct IpcJavascript<'a> { + pub(crate) isolation_origin: &'a str, +} + #[derive(Default)] /// Spaced and quoted Content-Security-Policy hash values. struct CspHashStrings { @@ -229,7 +240,7 @@ pub struct InnerWindowManager { /// Window event listeners to all windows. window_event_listeners: Arc>>, /// Responder for invoke calls. - invoke_responder: Arc>, + invoke_responder: Option>>, /// The script that initializes the invoke system. invoke_initialization_script: String, /// Application pattern. @@ -302,7 +313,7 @@ impl WindowManager { state: StateManager, window_event_listeners: Vec>, (menu, menu_event_listeners): (Option, Vec>), - (invoke_responder, invoke_initialization_script): (Arc>, String), + (invoke_responder, invoke_initialization_script): (Option>>, String), ) -> Self { // generate a random isolation key at runtime #[cfg(feature = "isolation")] @@ -353,7 +364,7 @@ impl WindowManager { } /// The invoke responder. - pub(crate) fn invoke_responder(&self) -> Arc> { + pub(crate) fn invoke_responder(&self) -> Option>> { self.inner.invoke_responder.clone() } @@ -506,6 +517,14 @@ impl WindowManager { registered_scheme_protocols.push("tauri".into()); } + if !registered_scheme_protocols.contains(&"ipc".into()) { + pending.register_uri_scheme_protocol( + "ipc", + crate::ipc::protocol::get(self.clone(), pending.label.clone()), + ); + registered_scheme_protocols.push("ipc".into()); + } + #[cfg(feature = "protocol-asset")] if !registered_scheme_protocols.contains(&"asset".into()) { let asset_scope = self.state().get::().asset_protocol.clone(); @@ -538,7 +557,7 @@ impl WindowManager { let asset = String::from_utf8_lossy(asset.as_ref()); let template = tauri_utils::pattern::isolation::IsolationJavascriptRuntime { runtime_aes_gcm_key: &aes_gcm_key, - stringify_ipc_message_fn: STRINGIFY_IPC_MESSAGE_FN, + process_ipc_message_fn: PROCESS_IPC_MESSAGE_FN, }; match template.render(asset.as_ref(), &Default::default()) { Ok(asset) => HttpResponseBuilder::new() @@ -567,43 +586,6 @@ impl WindowManager { Ok(pending) } - fn prepare_ipc_handler(&self) -> WebviewIpcHandler { - let manager = self.clone(); - Box::new(move |window, #[allow(unused_mut)] mut request| { - if let Some(window) = manager.get_window(&window.label) { - #[cfg(feature = "isolation")] - if let Pattern::Isolation { crypto_keys, .. } = manager.pattern() { - match RawIsolationPayload::try_from(request.as_str()) - .and_then(|raw| crypto_keys.decrypt(raw)) - { - Ok(json) => request = json, - Err(e) => { - let error: crate::Error = e.into(); - let _ = window.eval(&format!( - r#"console.error({})"#, - JsonValue::String(error.to_string()) - )); - return; - } - } - } - - match serde_json::from_str::(&request) { - Ok(message) => { - let _ = window.on_message(message); - } - Err(e) => { - let error: crate::Error = e.into(); - let _ = window.eval(&format!( - r#"console.error({})"#, - JsonValue::String(error.to_string()) - )); - } - } - } - }) - } - pub fn get_asset(&self, mut path: String) -> Result> { let assets = &self.inner.assets; if path.ends_with('/') { @@ -625,7 +607,7 @@ impl WindowManager { let asset_response = assets .get(&path.as_str().into()) .or_else(|| { - eprintln!("Asset `{path}` not found; fallback to {path}.html"); + debug_eprintln!("Asset `{path}` not found; fallback to {path}.html"); let fallback = format!("{}.html", path.as_str()).into(); let asset = assets.get(&fallback); asset_path = fallback; @@ -687,15 +669,11 @@ impl WindowManager { } } - #[allow(clippy::type_complexity)] fn prepare_uri_scheme_protocol( &self, window_origin: &str, - web_resource_request_handler: Option< - Box, - >, - ) -> Box Result> + Send + Sync> - { + web_resource_request_handler: Option>, + ) -> UriSchemeProtocolHandler { #[cfg(all(dev, mobile))] let url = { let mut url = self.get_url().as_str().to_string(); @@ -740,7 +718,6 @@ impl WindowManager { #[cfg(all(dev, mobile))] let mut response = { - use reqwest::StatusCode; let decoded_path = percent_encoding::percent_decode(path.as_bytes()) .decode_utf8_lossy() .to_string(); @@ -762,7 +739,7 @@ impl WindowManager { Ok(r) => { let mut response_cache_ = response_cache.lock().unwrap(); let mut response = None; - if r.status() == StatusCode::NOT_MODIFIED { + if r.status() == http::StatusCode::NOT_MODIFIED { response = response_cache_.get(&url); } let response = if let Some(r) = response { @@ -846,6 +823,12 @@ impl WindowManager { freeze_prototype: &'a str, } + #[derive(Template)] + #[default_template("../scripts/core.js")] + struct CoreJavascript<'a> { + os_name: &'a str, + } + let bundle_script = if with_global_tauri { include_str!("../scripts/bundle.global.js") } else { @@ -872,7 +855,11 @@ impl WindowManager { "window['_' + window.__TAURI__.transformCallback(cb) ]".into() ) ), - core_script: include_str!("../scripts/core.js"), + core_script: &CoreJavascript { + os_name: std::env::consts::OS, + } + .render_default(&Default::default())? + .into_string(), event_initialization_script: &self.event_initialization_script(), plugin_initialization_script, freeze_prototype, @@ -923,7 +910,7 @@ mod test { StateManager::new(), Default::default(), Default::default(), - (std::sync::Arc::new(|_, _, _, _| ()), "".into()), + (None, "".into()), ); #[cfg(custom_protocol)] @@ -1089,7 +1076,10 @@ impl WindowManager { #[allow(clippy::redundant_clone)] app_handle.clone(), )?; - pending.ipc_handler = Some(self.prepare_ipc_handler()); + #[cfg(not(ipc_custom_protocol))] + { + pending.ipc_handler = Some(crate::ipc::protocol::message_handler(self.clone())); + } // in `Windows`, we need to force a data_directory // but we do respect user-specification diff --git a/core/tauri/src/plugin.rs b/core/tauri/src/plugin.rs index a5cb7907e760..048b1935478e 100644 --- a/core/tauri/src/plugin.rs +++ b/core/tauri/src/plugin.rs @@ -5,8 +5,10 @@ //! The Tauri plugin extension to expand Tauri functionality. use crate::{ - utils::config::PluginConfig, AppHandle, Invoke, InvokeHandler, PageLoadPayload, RunEvent, - Runtime, Window, + app::PageLoadPayload, + ipc::{Invoke, InvokeHandler}, + utils::config::PluginConfig, + AppHandle, RunEvent, Runtime, Window, }; use serde::de::DeserializeOwned; use serde_json::Value as JsonValue; diff --git a/core/tauri/src/plugin/mobile.rs b/core/tauri/src/plugin/mobile.rs index 614aaf9aac7e..9b15f2cfce3c 100644 --- a/core/tauri/src/plugin/mobile.rs +++ b/core/tauri/src/plugin/mobile.rs @@ -4,7 +4,7 @@ use super::{PluginApi, PluginHandle}; -use crate::Runtime; +use crate::{ipc::Channel, AppHandle, Runtime}; #[cfg(target_os = "android")] use crate::{ runtime::RuntimeHandle, @@ -12,7 +12,7 @@ use crate::{ }; use once_cell::sync::OnceCell; -use serde::de::DeserializeOwned; +use serde::{de::DeserializeOwned, Serialize}; use std::{ collections::HashMap, @@ -20,11 +20,13 @@ use std::{ sync::{mpsc::channel, Mutex}, }; -type PendingPluginCallHandler = - Box) + Send + 'static>; +type PluginResponse = Result; + +type PendingPluginCallHandler = Box; static PENDING_PLUGIN_CALLS: OnceCell>> = OnceCell::new(); +static CHANNELS: OnceCell>> = OnceCell::new(); /// Possible errors when invoking a plugin. #[derive(Debug, thiserror::Error)] @@ -42,6 +44,17 @@ pub enum PluginInvokeError { /// Failed to deserialize response. #[error("failed to deserialize response: {0}")] CannotDeserializeResponse(serde_json::Error), + /// Failed to serialize request payload. + #[error("failed to serialize payload: {0}")] + CannotSerializePayload(serde_json::Error), +} + +pub(crate) fn register_channel(channel: Channel) { + CHANNELS + .get_or_init(Default::default) + .lock() + .unwrap() + .insert(channel.id(), channel); } /// Glue between Rust and the Kotlin code that sends the plugin response back. @@ -86,6 +99,26 @@ pub fn handle_android_plugin_response( } } +/// Glue between Rust and the Kotlin code that sends the channel data. +#[cfg(target_os = "android")] +pub fn send_channel_data( + env: jni::JNIEnv<'_>, + channel_id: i64, + data_str: jni::objects::JString<'_>, +) { + let data: serde_json::Value = + serde_json::from_str(env.get_string(data_str).unwrap().to_str().unwrap()).unwrap(); + + if let Some(channel) = CHANNELS + .get_or_init(Default::default) + .lock() + .unwrap() + .get(&(channel_id as u32)) + { + let _ = channel.send(data); + } +} + /// Error response from the Kotlin and Swift backends. #[derive(Debug, thiserror::Error, Clone, serde::Deserialize)] pub struct ErrorResponse { @@ -236,81 +269,22 @@ impl PluginApi { } impl PluginHandle { - /// Executes the given mobile method. - pub fn run_mobile_plugin( - &self, - method: impl AsRef, - payload: impl serde::Serialize, - ) -> Result { - #[cfg(target_os = "ios")] - { - self.run_ios_plugin(method, payload).map_err(Into::into) - } - #[cfg(target_os = "android")] - { - self.run_android_plugin(method, payload).map_err(Into::into) - } - } - - // Executes the given iOS method. - #[cfg(target_os = "ios")] - fn run_ios_plugin( + /// Executes the given mobile command. + pub fn run_mobile_plugin( &self, - method: impl AsRef, - payload: impl serde::Serialize, + command: impl AsRef, + payload: impl Serialize, ) -> Result { - use std::{ - ffi::CStr, - os::raw::{c_char, c_int}, - }; - - let id: i32 = rand::random(); let (tx, rx) = channel(); - PENDING_PLUGIN_CALLS - .get_or_init(Default::default) - .lock() - .unwrap() - .insert( - id, - Box::new(move |arg| { - tx.send(arg).unwrap(); - }), - ); - - unsafe { - extern "C" fn plugin_method_response_handler( - id: c_int, - success: c_int, - payload: *const c_char, - ) { - let payload = unsafe { - assert!(!payload.is_null()); - CStr::from_ptr(payload) - }; - - if let Some(handler) = PENDING_PLUGIN_CALLS - .get_or_init(Default::default) - .lock() - .unwrap() - .remove(&id) - { - let payload = serde_json::from_str(payload.to_str().unwrap()).unwrap(); - handler(if success == 1 { - Ok(payload) - } else { - Err(payload) - }); - } - } - - crate::ios::run_plugin_method( - id, - &self.name.into(), - &method.as_ref().into(), - crate::ios::json_to_dictionary(&serde_json::to_value(payload).unwrap()) as _, - crate::ios::PluginMessageCallback(plugin_method_response_handler), - ); - } + run_command( + self.name, + &self.handle, + command, + serde_json::to_value(payload).map_err(PluginInvokeError::CannotSerializePayload)?, + move |response| { + tx.send(response).unwrap(); + }, + )?; let response = rx.recv().unwrap(); match response { @@ -322,89 +296,154 @@ impl PluginHandle { ), } } +} - // Executes the given Android method. - #[cfg(target_os = "android")] - fn run_android_plugin( - &self, - method: impl AsRef, - payload: impl serde::Serialize, - ) -> Result { - use jni::{errors::Error as JniError, objects::JObject, JNIEnv}; +#[cfg(target_os = "ios")] +pub(crate) fn run_command, F: FnOnce(PluginResponse) + Send + 'static>( + name: &str, + _handle: &AppHandle, + command: C, + payload: serde_json::Value, + handler: F, +) -> Result<(), PluginInvokeError> { + use std::{ + ffi::CStr, + os::raw::{c_char, c_int, c_ulonglong}, + }; - fn run( - id: i32, - plugin: &'static str, - method: String, - payload: &serde_json::Value, - runtime_handle: &R::Handle, - env: JNIEnv<'_>, - activity: JObject<'_>, - ) -> Result<(), JniError> { - let data = crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, payload)?; - let plugin_manager = env - .call_method( - activity, - "getPluginManager", - "()Lapp/tauri/plugin/PluginManager;", - &[], - )? - .l()?; + let id: i32 = rand::random(); + PENDING_PLUGIN_CALLS + .get_or_init(Default::default) + .lock() + .unwrap() + .insert(id, Box::new(handler)); + + unsafe { + extern "C" fn plugin_command_response_handler( + id: c_int, + success: c_int, + payload: *const c_char, + ) { + let payload = unsafe { + assert!(!payload.is_null()); + CStr::from_ptr(payload) + }; - env.call_method( - plugin_manager, - "runCommand", - "(ILjava/lang/String;Ljava/lang/String;Lapp/tauri/plugin/JSObject;)V", - &[ - id.into(), - env.new_string(plugin)?.into(), - env.new_string(&method)?.into(), - data, - ], - )?; + if let Some(handler) = PENDING_PLUGIN_CALLS + .get_or_init(Default::default) + .lock() + .unwrap() + .remove(&id) + { + let payload = serde_json::from_str(payload.to_str().unwrap()).unwrap(); + handler(if success == 1 { + Ok(payload) + } else { + Err(payload) + }); + } + } - Ok(()) + extern "C" fn send_channel_data_handler(id: c_ulonglong, payload: *const c_char) { + let payload = unsafe { + assert!(!payload.is_null()); + CStr::from_ptr(payload) + }; + + if let Some(channel) = CHANNELS + .get_or_init(Default::default) + .lock() + .unwrap() + .get(&(id as u32)) + { + let payload: serde_json::Value = serde_json::from_str(payload.to_str().unwrap()).unwrap(); + let _ = channel.send(payload); + } } - let handle = match self.handle.runtime() { - RuntimeOrDispatch::Runtime(r) => r.handle(), - RuntimeOrDispatch::RuntimeHandle(h) => h, - _ => unreachable!(), - }; + crate::ios::run_plugin_command( + id, + &name.into(), + &command.as_ref().into(), + crate::ios::json_to_dictionary(&payload) as _, + crate::ios::PluginMessageCallback(plugin_command_response_handler), + crate::ios::ChannelSendDataCallback(send_channel_data_handler), + ); + } - let id: i32 = rand::random(); - let plugin_name = self.name; - let method = method.as_ref().to_string(); - let payload = serde_json::to_value(payload).unwrap(); - let handle_ = handle.clone(); + Ok(()) +} - let (tx, rx) = channel(); - let tx_ = tx.clone(); - PENDING_PLUGIN_CALLS - .get_or_init(Default::default) - .lock() - .unwrap() - .insert( - id, - Box::new(move |arg| { - tx.send(Ok(arg)).unwrap(); - }), - ); - - handle.run_on_android_context(move |env, activity, _webview| { - if let Err(e) = run::(id, plugin_name, method, &payload, &handle_, env, activity) { - tx_.send(Err(e)).unwrap(); - } - }); +#[cfg(target_os = "android")] +pub(crate) fn run_command< + R: Runtime, + C: AsRef, + F: FnOnce(PluginResponse) + Send + Clone + 'static, +>( + name: &str, + handle: &AppHandle, + command: C, + payload: serde_json::Value, + handler: F, +) -> Result<(), PluginInvokeError> { + use jni::{errors::Error as JniError, objects::JObject, JNIEnv}; + + fn run( + id: i32, + plugin: &str, + command: String, + payload: &serde_json::Value, + runtime_handle: &R::Handle, + env: JNIEnv<'_>, + activity: JObject<'_>, + ) -> Result<(), JniError> { + let data = crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, payload)?; + let plugin_manager = env + .call_method( + activity, + "getPluginManager", + "()Lapp/tauri/plugin/PluginManager;", + &[], + )? + .l()?; + + env.call_method( + plugin_manager, + "runCommand", + "(ILjava/lang/String;Ljava/lang/String;Lapp/tauri/plugin/JSObject;)V", + &[ + id.into(), + env.new_string(plugin)?.into(), + env.new_string(&command)?.into(), + data, + ], + )?; - let response = rx.recv().unwrap()?; - match response { - Ok(r) => serde_json::from_value(r).map_err(PluginInvokeError::CannotDeserializeResponse), - Err(r) => Err( - serde_json::from_value::(r) - .map(Into::into) - .map_err(PluginInvokeError::CannotDeserializeResponse)?, - ), - } + Ok(()) } + + let handle = match handle.runtime() { + RuntimeOrDispatch::Runtime(r) => r.handle(), + RuntimeOrDispatch::RuntimeHandle(h) => h, + _ => unreachable!(), + }; + + let id: i32 = rand::random(); + let plugin_name = name.to_string(); + let command = command.as_ref().to_string(); + let handle_ = handle.clone(); + + PENDING_PLUGIN_CALLS + .get_or_init(Default::default) + .lock() + .unwrap() + .insert(id, Box::new(handler.clone())); + + handle.run_on_android_context(move |env, activity, _webview| { + if let Err(e) = run::(id, &plugin_name, command, &payload, &handle_, env, activity) { + handler(Err(e.to_string().into())); + } + }); + + Ok(()) } diff --git a/core/tauri/src/scope/ipc.rs b/core/tauri/src/scope/ipc.rs index 5a0f6db27eff..b0eea0bbd172 100644 --- a/core/tauri/src/scope/ipc.rs +++ b/core/tauri/src/scope/ipc.rs @@ -168,9 +168,10 @@ impl Scope { mod tests { use super::RemoteDomainAccessScope; use crate::{ - api::ipc::CallbackFn, + ipc::CallbackFn, test::{assert_ipc_response, mock_app, MockRuntime}, - App, InvokePayload, Manager, Window, WindowBuilder, + window::InvokeRequest, + App, Manager, Window, WindowBuilder, }; const PLUGIN_NAME: &str = "test"; @@ -188,7 +189,7 @@ mod tests { (app, window) } - fn path_is_absolute_payload() -> InvokePayload { + fn path_is_absolute_request() -> InvokeRequest { let callback = CallbackFn(0); let error = CallbackFn(1); @@ -198,23 +199,25 @@ mod tests { serde_json::Value::String(std::env::current_dir().unwrap().display().to_string()), ); - InvokePayload { + InvokeRequest { cmd: "plugin:path|is_absolute".into(), callback, error, - inner: serde_json::Value::Object(payload), + body: serde_json::Value::Object(payload).into(), + headers: Default::default(), } } - fn plugin_test_payload() -> InvokePayload { + fn plugin_test_request() -> InvokeRequest { let callback = CallbackFn(0); let error = CallbackFn(1); - InvokePayload { + InvokeRequest { cmd: format!("plugin:{PLUGIN_NAME}|doSomething"), callback, error, - inner: Default::default(), + body: Default::default(), + headers: Default::default(), } } @@ -227,7 +230,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(&crate::window::ipc_scope_not_found_error_message( "main", "https://tauri.app/", @@ -244,7 +247,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(&crate::window::ipc_scope_window_error_message("main")), ); } @@ -258,7 +261,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(&crate::window::ipc_scope_domain_error_message( "https://tauri.app/", )), @@ -277,25 +280,25 @@ mod tests { ]); window.navigate("https://tauri.app".parse().unwrap()); - assert_ipc_response(&window, path_is_absolute_payload(), Ok(true)); + assert_ipc_response(&window, path_is_absolute_request(), Ok(true)); window.navigate("https://blog.tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(&crate::window::ipc_scope_domain_error_message( "https://blog.tauri.app/", )), ); window.navigate("https://sub.tauri.app".parse().unwrap()); - assert_ipc_response(&window, path_is_absolute_payload(), Ok(true)); + assert_ipc_response(&window, path_is_absolute_request(), Ok(true)); window.window.label = "test".into(); window.navigate("https://dev.tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(&crate::window::ipc_scope_not_found_error_message( "test", "https://dev.tauri.app/", @@ -310,7 +313,7 @@ mod tests { .add_plugin("path")]); window.navigate("https://tauri.app/inner/path".parse().unwrap()); - assert_ipc_response(&window, path_is_absolute_payload(), Ok(true)); + assert_ipc_response(&window, path_is_absolute_request(), Ok(true)); } #[test] @@ -322,7 +325,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - path_is_absolute_payload(), + path_is_absolute_request(), Err(crate::window::IPC_SCOPE_DOES_NOT_ALLOW), ); } @@ -336,7 +339,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - plugin_test_payload(), + plugin_test_request(), Err(&format!("plugin {PLUGIN_NAME} not found")), ); } @@ -350,7 +353,7 @@ mod tests { window.navigate("https://tauri.app".parse().unwrap()); assert_ipc_response( &window, - plugin_test_payload(), + plugin_test_request(), Err(crate::window::IPC_SCOPE_DOES_NOT_ALLOW), ); } diff --git a/core/tauri/src/state.rs b/core/tauri/src/state.rs index 03d74679b29c..098014234375 100644 --- a/core/tauri/src/state.rs +++ b/core/tauri/src/state.rs @@ -4,7 +4,8 @@ use crate::{ command::{CommandArg, CommandItem}, - InvokeError, Runtime, + ipc::InvokeError, + Runtime, }; use state::TypeMap; diff --git a/core/tauri/src/test/mod.rs b/core/tauri/src/test/mod.rs index 3884733d63ab..275699517508 100644 --- a/core/tauri/src/test/mod.rs +++ b/core/tauri/src/test/mod.rs @@ -42,11 +42,12 @@ //! // in this case we'll run the my_cmd command with no arguments //! tauri::test::assert_ipc_response( //! &window, -//! tauri::InvokePayload { +//! tauri::window::InvokeRequest { //! cmd: "my_cmd".into(), -//! callback: tauri::api::ipc::CallbackFn(0), -//! error: tauri::api::ipc::CallbackFn(1), -//! inner: serde_json::Value::Null, +//! callback: tauri::ipc::CallbackFn(0), +//! error: tauri::ipc::CallbackFn(1), +//! body: serde_json::Value::Null.into(), +//! headers: Default::default(), //! }, //! Ok(()) //! ); @@ -59,21 +60,19 @@ mod mock_runtime; pub use mock_runtime::*; use serde::Serialize; -use serde_json::Value as JsonValue; use std::{ borrow::Cow, - collections::HashMap, fmt::Debug, hash::{Hash, Hasher}, - sync::{ - mpsc::{channel, Sender}, - Arc, Mutex, - }, + sync::Arc, }; -use crate::hooks::window_invoke_responder; -use crate::{api::ipc::CallbackFn, App, Builder, Context, InvokePayload, Manager, Pattern, Window}; +use crate::{ + ipc::{CallbackFn, InvokeResponse}, + window::InvokeRequest, + App, Builder, Context, Pattern, Window, +}; use tauri_utils::{ assets::{AssetKey, Assets, CspHash}, config::{Config, PatternKind, TauriConfig}, @@ -92,8 +91,6 @@ impl Hash for IpcKey { } } -struct Ipc(Mutex>>>); - /// An empty [`Assets`] implementation. pub struct NoopAsset { csp_hashes: Vec>, @@ -166,20 +163,7 @@ pub fn mock_context(assets: A) -> crate::Context { /// } /// ``` pub fn mock_builder() -> Builder { - let mut builder = Builder::::new().manage(Ipc(Default::default())); - - builder.invoke_responder = Arc::new(|window, response, callback, error| { - let window_ = window.clone(); - let ipc = window_.state::(); - let mut ipc_ = ipc.0.lock().unwrap(); - if let Some(tx) = ipc_.remove(&IpcKey { callback, error }) { - tx.send(response.into_result()).unwrap(); - } else { - window_invoke_responder(window, response, callback, error) - } - }); - - builder + Builder::::new() } /// Creates a new [`App`] for testing using the [`mock_context`] with a [`noop_assets`]. @@ -222,11 +206,12 @@ pub fn mock_app() -> App { /// // run the `ping` command and assert it returns `pong` /// tauri::test::assert_ipc_response( /// &window, -/// tauri::InvokePayload { +/// tauri::window::InvokeRequest { /// cmd: "ping".into(), -/// callback: tauri::api::ipc::CallbackFn(0), -/// error: tauri::api::ipc::CallbackFn(1), -/// inner: serde_json::Value::Null, +/// callback: tauri::ipc::CallbackFn(0), +/// error: tauri::ipc::CallbackFn(1), +/// body: serde_json::Value::Null.into(), +/// headers: Default::default(), /// }, /// // the expected response is a success with the "pong" payload /// // we could also use Err("error message") here to ensure the command failed @@ -237,18 +222,17 @@ pub fn mock_app() -> App { /// ``` pub fn assert_ipc_response( window: &Window, - payload: InvokePayload, + request: InvokeRequest, expected: Result, ) { - let callback = payload.callback; - let error = payload.error; - let ipc = window.state::(); - let (tx, rx) = channel(); - ipc.0.lock().unwrap().insert(IpcKey { callback, error }, tx); - window.clone().on_message(payload).unwrap(); + let rx = window.clone().on_message(request); + let response = rx.recv().unwrap(); assert_eq!( - rx.recv().unwrap(), + match response { + InvokeResponse::Ok(b) => Ok(b.into_json()), + InvokeResponse::Err(e) => Err(e.0), + }, expected .map(|e| serde_json::to_value(e).unwrap()) .map_err(|e| serde_json::to_value(e).unwrap()) diff --git a/core/tauri/src/window.rs b/core/tauri/src/window.rs index df111afb680c..03678ab3690d 100644 --- a/core/tauri/src/window.rs +++ b/core/tauri/src/window.rs @@ -6,6 +6,7 @@ pub(crate) mod menu; +use http::HeaderMap; pub use menu::{MenuEvent, MenuHandle}; pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState}; use url::Url; @@ -13,11 +14,12 @@ use url::Url; #[cfg(target_os = "macos")] use crate::TitleBarStyle; use crate::{ - api::ipc::CallbackFn, app::AppHandle, command::{CommandArg, CommandItem}, event::{Event, EventHandler}, - hooks::{InvokePayload, InvokeResponder}, + ipc::{ + CallbackFn, Invoke, InvokeBody, InvokeError, InvokeMessage, InvokeResolver, InvokeResponse, + }, manager::WindowManager, runtime::{ http::{Request as HttpRequest, Response as HttpResponse}, @@ -32,8 +34,7 @@ use crate::{ sealed::ManagerBase, sealed::RuntimeOrDispatch, utils::config::{WindowConfig, WindowEffectsConfig, WindowUrl}, - EventLoopMessage, Invoke, InvokeError, InvokeMessage, InvokeResolver, Manager, PageLoadPayload, - Runtime, Theme, WindowEvent, + EventLoopMessage, Manager, Runtime, Theme, WindowEvent, }; #[cfg(desktop)] use crate::{ @@ -56,11 +57,16 @@ use std::{ fmt, hash::{Hash, Hasher}, path::PathBuf, - sync::{Arc, Mutex}, + sync::{ + mpsc::{sync_channel, Receiver}, + Arc, Mutex, + }, }; pub(crate) type WebResourceRequestHandler = dyn Fn(&HttpRequest, &mut HttpResponse) + Send + Sync; pub(crate) type NavigationHandler = dyn Fn(&Url) -> bool + Send; +pub(crate) type UriSchemeProtocolHandler = + Box Result> + Send + Sync>; #[derive(Clone, Serialize)] struct WindowCreatedEvent { @@ -772,6 +778,21 @@ struct JsEventListenerKey { pub event: String, } +/// The IPC invoke request. +#[derive(Debug)] +pub struct InvokeRequest { + /// The invoke command. + pub cmd: String, + /// The success callback. + pub callback: CallbackFn, + /// The error callback. + pub error: CallbackFn, + /// The body of the request. + pub body: InvokeBody, + /// The request headers. + pub headers: HeaderMap, +} + // TODO: expand these docs since this is a pretty important type /// A webview window managed by Tauri. /// @@ -967,10 +988,6 @@ impl Window { WindowBuilder::<'a, R>::new(manager, label.into(), url) } - pub(crate) fn invoke_responder(&self) -> Arc> { - self.manager.invoke_responder() - } - /// The current window's dispatcher. pub(crate) fn dispatcher(&self) -> R::Dispatcher { self.window.dispatcher.clone() @@ -1662,14 +1679,17 @@ impl Window { } fn is_local_url(&self, current_url: &Url) -> bool { - self.manager.get_url().make_relative(current_url).is_some() || { - let protocol_url = self.manager.protocol_url(); - current_url.scheme() == protocol_url.scheme() && current_url.domain() == protocol_url.domain() - } + self.manager.get_url().make_relative(current_url).is_some() + || { + let protocol_url = self.manager.protocol_url(); + current_url.scheme() == protocol_url.scheme() + && current_url.domain() == protocol_url.domain() + } + || (cfg!(dev) && current_url.domain() == Some("tauri.localhost")) } - /// Handles this window receiving an [`InvokeMessage`]. - pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> { + /// Handles this window receiving an [`InvokeRequest`]. + pub fn on_message(self, request: InvokeRequest) -> Receiver { let manager = self.manager.clone(); let current_url = self.url(); let is_local = self.is_local_url(¤t_url); @@ -1691,39 +1711,108 @@ impl Window { } } }; - match payload.cmd.as_str() { - "__initialized" => { - let payload: PageLoadPayload = serde_json::from_value(payload.inner)?; - manager.run_on_page_load(self, payload); - } + + let (tx, rx) = sync_channel(1); + + let custom_responder = self.manager.invoke_responder(); + + let resolver = InvokeResolver::new( + self.clone(), + Arc::new( + #[allow(unused_variables)] + move |window: Window, cmd, response, callback, error| { + #[cfg(not(ipc_custom_protocol))] + { + use crate::ipc::{ + format_callback::{ + format as format_callback, format_result as format_callback_result, + }, + Channel, + }; + use serde_json::Value as JsonValue; + + // the channel data command is the only command that uses a custom protocol on Linux + if custom_responder.is_none() && cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND + { + fn responder_eval( + window: &Window, + js: crate::api::Result, + error: CallbackFn, + ) { + let eval_js = match js { + Ok(js) => js, + Err(e) => format_callback(error, &e.to_string()) + .expect("unable to serialize response error string to json"), + }; + + let _ = window.eval(&eval_js); + } + + match &response { + InvokeResponse::Ok(InvokeBody::Json(v)) => { + if matches!(v, JsonValue::Object(_) | JsonValue::Array(_)) { + let _ = Channel::from_ipc(window.clone(), callback).send(v); + } else { + responder_eval( + &window, + format_callback_result(Result::<_, ()>::Ok(v), callback, error), + error, + ) + } + } + InvokeResponse::Ok(InvokeBody::Raw(v)) => { + let _ = + Channel::from_ipc(window.clone(), callback).send(InvokeBody::Raw(v.clone())); + } + InvokeResponse::Err(e) => responder_eval( + &window, + format_callback_result(Result::<(), _>::Err(&e.0), callback, error), + error, + ), + } + } + } + + if let Some(responder) = &custom_responder { + (responder)(window, cmd, &response, callback, error); + } + + let _ = tx.send(response); + }, + ), + request.cmd.clone(), + request.callback, + request.error, + ); + + match request.cmd.as_str() { + "__initialized" => match request.body.deserialize() { + Ok(payload) => { + manager.run_on_page_load(self, payload); + resolver.resolve(()); + } + Err(e) => resolver.reject(e.to_string()), + }, _ => { + #[cfg(mobile)] + let app_handle = self.app_handle.clone(); + let message = InvokeMessage::new( - self.clone(), + self, manager.state(), - payload.cmd.to_string(), - payload.inner, + request.cmd.to_string(), + request.body, + request.headers, ); - #[allow(clippy::redundant_clone)] - let resolver = InvokeResolver::new(self.clone(), payload.callback, payload.error); - let mut invoke = Invoke { message, resolver }; + let mut invoke = Invoke { + message, + resolver: resolver.clone(), + }; + if !is_local && scope.is_none() { invoke.resolver.reject(scope_not_found_error_message); - return Ok(()); - } - - if payload.cmd.starts_with("plugin:") { - if !is_local { - let command = invoke.message.command.replace("plugin:", ""); - let plugin_name = command.split('|').next().unwrap().to_string(); - if !scope - .map(|s| s.plugins().contains(&plugin_name)) - .unwrap_or(true) - { - invoke.resolver.reject(IPC_SCOPE_DOES_NOT_ALLOW); - return Ok(()); - } - } + } else if request.cmd.starts_with("plugin:") { let command = invoke.message.command.replace("plugin:", ""); let mut tokens = command.split('|'); // safe to unwrap: split always has a least one item @@ -1733,101 +1822,59 @@ impl Window { .map(|c| c.to_string()) .unwrap_or_else(String::new); + if !(is_local + || plugin == crate::ipc::channel::CHANNEL_PLUGIN_NAME + || scope + .map(|s| s.plugins().contains(&plugin.into())) + .unwrap_or(true)) + { + invoke.resolver.reject(IPC_SCOPE_DOES_NOT_ALLOW); + return rx; + } + let command = invoke.message.command.clone(); - let resolver = invoke.resolver.clone(); + #[cfg(mobile)] let message = invoke.message.clone(); #[allow(unused_mut)] let mut handled = manager.extend_api(plugin, invoke); - #[cfg(target_os = "ios")] + #[cfg(mobile)] { if !handled { handled = true; - let plugin = plugin.to_string(); - let (callback, error) = (resolver.callback, resolver.error); - self.with_webview(move |webview| { - unsafe { - crate::ios::post_ipc_message( - webview.inner() as _, - &plugin.as_str().into(), - &heck::ToLowerCamelCase::to_lower_camel_case(message.command.as_str()) - .as_str() - .into(), - crate::ios::json_to_dictionary(&message.payload) as _, - callback.0, - error.0, - ) - }; - })?; - } - } - #[cfg(target_os = "android")] - { - if !handled { - handled = true; - let resolver_ = resolver.clone(); - let runtime_handle = self.app_handle.runtime_handle.clone(); - let plugin = plugin.to_string(); - self.with_webview(move |webview| { - webview.jni_handle().exec(move |env, activity, webview| { - use jni::{ - errors::Error as JniError, - objects::JObject, - JNIEnv, - }; - - fn handle_message( - plugin: &str, - runtime_handle: &R::Handle, - message: InvokeMessage, - (callback, error): (CallbackFn, CallbackFn), - env: JNIEnv<'_>, - activity: JObject<'_>, - webview: JObject<'_>, - ) -> Result<(), JniError> { - let data = crate::jni_helpers::to_jsobject::(env, activity, runtime_handle, &message.payload)?; - let plugin_manager = env - .call_method( - activity, - "getPluginManager", - "()Lapp/tauri/plugin/PluginManager;", - &[], - )? - .l()?; - - env.call_method( - plugin_manager, - "postIpcMessage", - "(Landroid/webkit/WebView;Ljava/lang/String;Ljava/lang/String;Lapp/tauri/plugin/JSObject;JJ)V", - &[ - webview.into(), - env.new_string(plugin)?.into(), - env.new_string(&heck::ToLowerCamelCase::to_lower_camel_case(message.command.as_str()))?.into(), - data, - (callback.0 as i64).into(), - (error.0 as i64).into(), - ], - )?; - - Ok(()) + fn load_channels(payload: &serde_json::Value, window: &Window) { + if let serde_json::Value::Object(map) = payload { + for v in map.values() { + if let serde_json::Value::String(s) = v { + if s.starts_with(crate::ipc::channel::IPC_PAYLOAD_PREFIX) { + crate::ipc::Channel::load_from_ipc(window.clone(), s); + } + } } + } + } - if let Err(e) = handle_message( - &plugin, - &runtime_handle, - message, - (resolver_.callback, resolver_.error), - env, - activity, - webview, - ) { - resolver_.reject(format!("failed to reach Android layer: {e}")); - } - }); - })?; + let payload = message.payload.into_json(); + // initialize channels + load_channels(&payload, &message.window); + + let resolver_ = resolver.clone(); + if let Err(e) = crate::plugin::mobile::run_command( + plugin, + &app_handle, + message.command, + payload, + move |response| match response { + Ok(r) => resolver_.resolve(r), + Err(e) => resolver_.reject(e), + }, + ) { + resolver.reject(e.to_string()); + return rx; + } } } @@ -1836,7 +1883,6 @@ impl Window { } } else { let command = invoke.message.command.clone(); - let resolver = invoke.resolver.clone(); let handled = manager.run_invoke_handler(invoke); if !handled { resolver.reject(format!("Command {command} not found")); @@ -1845,7 +1891,7 @@ impl Window { } } - Ok(()) + rx } /// Evaluates JavaScript on this window. diff --git a/core/tauri/test/fixture/src-tauri/tauri.conf.json b/core/tauri/test/fixture/src-tauri/tauri.conf.json index 147743e3c13c..facec1a219b4 100644 --- a/core/tauri/test/fixture/src-tauri/tauri.conf.json +++ b/core/tauri/test/fixture/src-tauri/tauri.conf.json @@ -15,7 +15,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/api/dist/assets/index.js b/examples/api/dist/assets/index.js index 0ee0c82e2d75..b7c96e9f0849 100644 --- a/examples/api/dist/assets/index.js +++ b/examples/api/dist/assets/index.js @@ -1,9 +1,9 @@ -(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))i(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const m of a.addedNodes)m.tagName==="LINK"&&m.rel==="modulepreload"&&i(m)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerpolicy&&(a.referrerPolicy=r.referrerpolicy),r.crossorigin==="use-credentials"?a.credentials="include":r.crossorigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();function $(){}function st(e){return e()}function Xe(){return Object.create(null)}function V(e){e.forEach(st)}function vt(e){return typeof e=="function"}function he(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let ke;function bt(e,t){return ke||(ke=document.createElement("a")),ke.href=t,e===ke.href}function yt(e){return Object.keys(e).length===0}function wt(e,...t){if(e==null)return $;const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function kt(e,t,n){e.$$.on_destroy.push(wt(t,n))}function o(e,t){e.appendChild(t)}function k(e,t,n){e.insertBefore(t,n||null)}function w(e){e.parentNode.removeChild(e)}function Ye(e,t){for(let n=0;ne.removeEventListener(t,n,i)}function l(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function $t(e){return Array.from(e.childNodes)}function Lt(e,t){t=""+t,e.wholeText!==t&&(e.data=t)}class xt{constructor(t=!1){this.is_svg=!1,this.is_svg=t,this.e=this.n=null}c(t){this.h(t)}m(t,n,i=null){this.e||(this.is_svg?this.e=Et(n.nodeName):this.e=f(n.nodeName),this.t=n,this.c(t)),this.i(i)}h(t){this.e.innerHTML=t,this.n=Array.from(this.e.childNodes)}i(t){for(let n=0;n{Le.delete(e),i&&(n&&e.d(1),i())}),e.o(t)}else i&&i()}function Qe(e){e&&e.c()}function Me(e,t,n,i){const{fragment:r,on_mount:a,on_destroy:m,after_update:c}=e.$$;r&&r.m(t,n),i||We(()=>{const u=a.map(st).filter(vt);m?m.push(...u):V(u),e.$$.on_mount=[]}),c.forEach(We)}function Re(e,t){const n=e.$$;n.fragment!==null&&(V(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function Nt(e,t){e.$$.dirty[0]===-1&&(ce.push(e),Ot(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const O=H.length?H[0]:S;return d.ctx&&r(d.ctx[v],d.ctx[v]=O)&&(!d.skip_bound&&d.bound[v]&&d.bound[v](O),E&&Nt(e,v)),S}):[],d.update(),E=!0,V(d.before_update),d.fragment=i?i(d.ctx):!1,t.target){if(t.hydrate){const v=$t(t.target);d.fragment&&d.fragment.l(v),v.forEach(w)}else d.fragment&&d.fragment.c();t.intro&&Ae(e.$$.fragment),Me(e,t.target,t.anchor,t.customElement),ut()}ue(u)}class Oe{$destroy(){Re(this,1),this.$destroy=$}$on(t,n){const i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{const r=i.indexOf(n);r!==-1&&i.splice(r,1)}}$set(t){this.$$set&&!yt(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const K=[];function It(e,t=$){let n;const i=new Set;function r(c){if(he(e,c)&&(e=c,n)){const u=!K.length;for(const d of i)d[1](),K.push(d,e);if(u){for(let d=0;d{i.delete(d),i.size===0&&(n(),n=null)}}return{set:r,update:a,subscribe:m}}function Wt(e){let t;return{c(){t=f("p"),t.innerHTML=`This is a demo of Tauri's API capabilities using the @tauri-apps/api package. It's used as the main validation app, serving as the test bed of our +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))i(r);new MutationObserver(r=>{for(const a of r)if(a.type==="childList")for(const p of a.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&i(p)}).observe(document,{childList:!0,subtree:!0});function n(r){const a={};return r.integrity&&(a.integrity=r.integrity),r.referrerpolicy&&(a.referrerPolicy=r.referrerpolicy),r.crossorigin==="use-credentials"?a.credentials="include":r.crossorigin==="anonymous"?a.credentials="omit":a.credentials="same-origin",a}function i(r){if(r.ep)return;r.ep=!0;const a=n(r);fetch(r.href,a)}})();function $(){}function st(e){return e()}function Xe(){return Object.create(null)}function V(e){e.forEach(st)}function vt(e){return typeof e=="function"}function he(e,t){return e!=e?t==t:e!==t||e&&typeof e=="object"||typeof e=="function"}let ke;function bt(e,t){return ke||(ke=document.createElement("a")),ke.href=t,e===ke.href}function yt(e){return Object.keys(e).length===0}function wt(e,...t){if(e==null)return $;const n=e.subscribe(...t);return n.unsubscribe?()=>n.unsubscribe():n}function kt(e,t,n){e.$$.on_destroy.push(wt(t,n))}function o(e,t){e.appendChild(t)}function k(e,t,n){e.insertBefore(t,n||null)}function w(e){e.parentNode.removeChild(e)}function Ye(e,t){for(let n=0;ne.removeEventListener(t,n,i)}function c(e,t,n){n==null?e.removeAttribute(t):e.getAttribute(t)!==n&&e.setAttribute(t,n)}function $t(e){return Array.from(e.childNodes)}function Lt(e,t){t=""+t,e.wholeText!==t&&(e.data=t)}class xt{constructor(t=!1){this.is_svg=!1,this.is_svg=t,this.e=this.n=null}c(t){this.h(t)}m(t,n,i=null){this.e||(this.is_svg?this.e=Et(n.nodeName):this.e=f(n.nodeName),this.t=n,this.c(t)),this.i(i)}h(t){this.e.innerHTML=t,this.n=Array.from(this.e.childNodes)}i(t){for(let n=0;n{Le.delete(e),i&&(n&&e.d(1),i())}),e.o(t)}else i&&i()}function Qe(e){e&&e.c()}function Me(e,t,n,i){const{fragment:r,on_mount:a,on_destroy:p,after_update:s}=e.$$;r&&r.m(t,n),i||We(()=>{const u=a.map(st).filter(vt);p?p.push(...u):V(u),e.$$.on_mount=[]}),s.forEach(We)}function Re(e,t){const n=e.$$;n.fragment!==null&&(V(n.on_destroy),n.fragment&&n.fragment.d(t),n.on_destroy=n.fragment=null,n.ctx=[])}function Nt(e,t){e.$$.dirty[0]===-1&&(ce.push(e),Ot(),e.$$.dirty.fill(0)),e.$$.dirty[t/31|0]|=1<{const O=H.length?H[0]:S;return d.ctx&&r(d.ctx[v],d.ctx[v]=O)&&(!d.skip_bound&&d.bound[v]&&d.bound[v](O),E&&Nt(e,v)),S}):[],d.update(),E=!0,V(d.before_update),d.fragment=i?i(d.ctx):!1,t.target){if(t.hydrate){const v=$t(t.target);d.fragment&&d.fragment.l(v),v.forEach(w)}else d.fragment&&d.fragment.c();t.intro&&Ae(e.$$.fragment),Me(e,t.target,t.anchor,t.customElement),ut()}ue(u)}class Oe{$destroy(){Re(this,1),this.$destroy=$}$on(t,n){const i=this.$$.callbacks[t]||(this.$$.callbacks[t]=[]);return i.push(n),()=>{const r=i.indexOf(n);r!==-1&&i.splice(r,1)}}$set(t){this.$$set&&!yt(t)&&(this.$$.skip_bound=!0,this.$$set(t),this.$$.skip_bound=!1)}}const K=[];function It(e,t=$){let n;const i=new Set;function r(s){if(he(e,s)&&(e=s,n)){const u=!K.length;for(const d of i)d[1](),K.push(d,e);if(u){for(let d=0;d{i.delete(d),i.size===0&&(n(),n=null)}}return{set:r,update:a,subscribe:p}}function Wt(e){let t;return{c(){t=f("p"),t.innerHTML=`This is a demo of Tauri's API capabilities using the @tauri-apps/api package. It's used as the main validation app, serving as the test bed of our development process. In the future, this app will be used on Tauri's integration - tests.`},m(n,i){k(n,t,i)},p:$,i:$,o:$,d(n){n&&w(t)}}}class At extends Oe{constructor(t){super(),Se(this,t,null,Wt,he,{})}}var Mt=Object.defineProperty,dt=(e,t)=>{for(var n in t)Mt(e,n,{get:t[n],enumerable:!0})},ft=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)},Ze=(e,t,n)=>(ft(e,t,"read from private field"),n?n.call(e):t.get(e)),Rt=(e,t,n)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,n)},Pt=(e,t,n,i)=>(ft(e,t,"write to private field"),i?i.call(e,n):t.set(e,n),n),Ht={};dt(Ht,{Channel:()=>ht,PluginListener:()=>mt,addPluginListener:()=>qt,convertFileSrc:()=>Ut,invoke:()=>P,transformCallback:()=>fe});function jt(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function fe(e,t=!1){let n=jt(),i=`_${n}`;return Object.defineProperty(window,i,{value:r=>(t&&Reflect.deleteProperty(window,i),e==null?void 0:e(r)),writable:!1,configurable:!0}),n}var ae,ht=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,Rt(this,ae,()=>{}),this.id=fe(e=>{Ze(this,ae).call(this,e)})}set onmessage(e){Pt(this,ae,e)}get onmessage(){return Ze(this,ae)}toJSON(){return`__CHANNEL__:${this.id}`}};ae=new WeakMap;var mt=class{constructor(e,t,n){this.plugin=e,this.event=t,this.channelId=n}async unregister(){return P(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function qt(e,t,n){let i=new ht;return i.onmessage=n,P(`plugin:${e}|register_listener`,{event:t,handler:i}).then(()=>new mt(e,t,i.id))}async function P(e,t={}){return new Promise((n,i)=>{let r=fe(m=>{n(m),Reflect.deleteProperty(window,`_${a}`)},!0),a=fe(m=>{i(m),Reflect.deleteProperty(window,`_${r}`)},!0);window.__TAURI_IPC__({cmd:e,callback:r,error:a,...t})})}function Ut(e,t="asset"){let n=encodeURIComponent(e);return navigator.userAgent.includes("Windows")?`https://${t}.localhost/${n}`:`${t}://localhost/${n}`}var zt={};dt(zt,{TauriEvent:()=>pt,emit:()=>_t,listen:()=>Pe,once:()=>Ft});var pt=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e))(pt||{});async function gt(e,t){await P("plugin:event|unlisten",{event:e,eventId:t})}async function Pe(e,t,n){return P("plugin:event|listen",{event:e,windowLabel:n==null?void 0:n.target,handler:fe(t)}).then(i=>async()=>gt(e,i))}async function Ft(e,t,n){return Pe(e,i=>{t(i),gt(e,i.id).catch(()=>{})},n)}async function _t(e,t,n){await P("plugin:event|emit",{event:e,windowLabel:n==null?void 0:n.target,payload:t})}function Vt(e){let t,n,i,r,a,m,c,u;return{c(){t=f("div"),n=f("button"),n.textContent="Call Log API",i=g(),r=f("button"),r.textContent="Call Request (async) API",a=g(),m=f("button"),m.textContent="Send event to Rust",l(n,"class","btn"),l(n,"id","log"),l(r,"class","btn"),l(r,"id","request"),l(m,"class","btn"),l(m,"id","event")},m(d,E){k(d,t,E),o(t,n),o(t,i),o(t,r),o(t,a),o(t,m),c||(u=[F(n,"click",e[0]),F(r,"click",e[1]),F(m,"click",e[2])],c=!0)},p:$,i:$,o:$,d(d){d&&w(t),c=!1,V(u)}}}function Bt(e,t,n){let{onMessage:i}=t,r;xe(async()=>{r=await Pe("rust-event",i)}),at(()=>{r&&r()});function a(){P("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function m(){P("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function c(){_t("js-event","this is the payload string")}return e.$$set=u=>{"onMessage"in u&&n(3,i=u.onMessage)},[a,m,c,i]}class Gt extends Oe{constructor(t){super(),Se(this,t,Bt,Vt,he,{onMessage:3})}}function Xt(e){let t;return{c(){t=f("div"),t.innerHTML=`
Not available for Linux
- `,l(t,"class","flex flex-col gap-2")},m(n,i){k(n,t,i)},p:$,i:$,o:$,d(n){n&&w(t)}}}function Yt(e,t,n){let{onMessage:i}=t;const r=window.constraints={audio:!0,video:!0};function a(c){const u=document.querySelector("video"),d=c.getVideoTracks();i("Got stream with constraints:",r),i(`Using video device: ${d[0].label}`),window.stream=c,u.srcObject=c}function m(c){if(c.name==="ConstraintNotSatisfiedError"){const u=r.video;i(`The resolution ${u.width.exact}x${u.height.exact} px is not supported by your device.`)}else c.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${c.name}`,c)}return xe(async()=>{try{const c=await navigator.mediaDevices.getUserMedia(r);a(c)}catch(c){m(c)}}),at(()=>{window.stream.getTracks().forEach(function(c){c.stop()})}),e.$$set=c=>{"onMessage"in c&&n(0,i=c.onMessage)},[i]}class Jt extends Oe{constructor(t){super(),Se(this,t,Yt,Xt,he,{onMessage:0})}}function et(e,t,n){const i=e.slice();return i[25]=t[n],i}function tt(e,t,n){const i=e.slice();return i[28]=t[n],i}function Kt(e){let t;return{c(){t=f("span"),l(t,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){k(n,t,i)},d(n){n&&w(t)}}}function Qt(e){let t;return{c(){t=f("span"),l(t,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){k(n,t,i)},d(n){n&&w(t)}}}function Zt(e){let t,n;return{c(){t=Q(`Switch to Dark mode - `),n=f("div"),l(n,"class","i-ph-moon")},m(i,r){k(i,t,r),k(i,n,r)},d(i){i&&w(t),i&&w(n)}}}function en(e){let t,n;return{c(){t=Q(`Switch to Light mode - `),n=f("div"),l(n,"class","i-ph-sun")},m(i,r){k(i,t,r),k(i,n,r)},d(i){i&&w(t),i&&w(n)}}}function tn(e){let t,n,i,r,a=e[28].label+"",m,c,u,d;function E(){return e[14](e[28])}return{c(){t=f("a"),n=f("div"),i=g(),r=f("p"),m=Q(a),l(n,"class",e[28].icon+" mr-2"),l(t,"href","##"),l(t,"class",c="nv "+(e[1]===e[28]?"nv_selected":""))},m(v,S){k(v,t,S),o(t,n),o(t,i),o(t,r),o(r,m),u||(d=F(t,"click",E),u=!0)},p(v,S){e=v,S&2&&c!==(c="nv "+(e[1]===e[28]?"nv_selected":""))&&l(t,"class",c)},d(v){v&&w(t),u=!1,d()}}}function nt(e){let t,n=e[28]&&tn(e);return{c(){n&&n.c(),t=lt()},m(i,r){n&&n.m(i,r),k(i,t,r)},p(i,r){i[28]&&n.p(i,r)},d(i){n&&n.d(i),i&&w(t)}}}function it(e){let t,n=e[25].html+"",i;return{c(){t=new xt(!1),i=lt(),t.a=i},m(r,a){t.m(n,r,a),k(r,i,a)},p(r,a){a&16&&n!==(n=r[25].html+"")&&t.p(n)},d(r){r&&w(i),r&&t.d()}}}function nn(e){let t,n,i,r,a,m,c,u,d,E,v,S,H,O,Z,I,me,b,j,C,q,B,ee,te,pe,ge,p,_,D,W,A,ne,U=e[1].label+"",Te,He,_e,ie,y,je,N,ve,qe,G,be,Ue,re,ze,oe,se,Ce,Fe;function Ve(s,T){return s[0]?Qt:Kt}let ye=Ve(e),M=ye(e);function Be(s,T){return s[2]?en:Zt}let we=Be(e),R=we(e),X=e[5],L=[];for(let s=0;s{for(var n in t)Mt(e,n,{get:t[n],enumerable:!0})},ft=(e,t,n)=>{if(!t.has(e))throw TypeError("Cannot "+n)},Ze=(e,t,n)=>(ft(e,t,"read from private field"),n?n.call(e):t.get(e)),Rt=(e,t,n)=>{if(t.has(e))throw TypeError("Cannot add the same private member more than once");t instanceof WeakSet?t.add(e):t.set(e,n)},Pt=(e,t,n,i)=>(ft(e,t,"write to private field"),i?i.call(e,n):t.set(e,n),n),Ht={};dt(Ht,{Channel:()=>ht,PluginListener:()=>mt,addPluginListener:()=>qt,convertFileSrc:()=>Ut,invoke:()=>P,transformCallback:()=>fe});function jt(){return window.crypto.getRandomValues(new Uint32Array(1))[0]}function fe(e,t=!1){let n=jt(),i=`_${n}`;return Object.defineProperty(window,i,{value:r=>(t&&Reflect.deleteProperty(window,i),e==null?void 0:e(r)),writable:!1,configurable:!0}),n}var ae,ht=class{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,Rt(this,ae,()=>{}),this.id=fe(e=>{Ze(this,ae).call(this,e)})}set onmessage(e){Pt(this,ae,e)}get onmessage(){return Ze(this,ae)}toJSON(){return`__CHANNEL__:${this.id}`}};ae=new WeakMap;var mt=class{constructor(e,t,n){this.plugin=e,this.event=t,this.channelId=n}async unregister(){return P(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}};async function qt(e,t,n){let i=new ht;return i.onmessage=n,P(`plugin:${e}|register_listener`,{event:t,handler:i}).then(()=>new mt(e,t,i.id))}async function P(e,t={},n){return new Promise((i,r)=>{let a=fe(s=>{i(s),Reflect.deleteProperty(window,`_${p}`)},!0),p=fe(s=>{r(s),Reflect.deleteProperty(window,`_${a}`)},!0);window.__TAURI_IPC__({cmd:e,callback:a,error:p,payload:t,options:n})})}function Ut(e,t="asset"){return window.__TAURI__.convertFileSrc(e,t)}var Ft={};dt(Ft,{TauriEvent:()=>pt,emit:()=>_t,listen:()=>Pe,once:()=>zt});var pt=(e=>(e.WINDOW_RESIZED="tauri://resize",e.WINDOW_MOVED="tauri://move",e.WINDOW_CLOSE_REQUESTED="tauri://close-requested",e.WINDOW_CREATED="tauri://window-created",e.WINDOW_DESTROYED="tauri://destroyed",e.WINDOW_FOCUS="tauri://focus",e.WINDOW_BLUR="tauri://blur",e.WINDOW_SCALE_FACTOR_CHANGED="tauri://scale-change",e.WINDOW_THEME_CHANGED="tauri://theme-changed",e.WINDOW_FILE_DROP="tauri://file-drop",e.WINDOW_FILE_DROP_HOVER="tauri://file-drop-hover",e.WINDOW_FILE_DROP_CANCELLED="tauri://file-drop-cancelled",e.MENU="tauri://menu",e))(pt||{});async function gt(e,t){await P("plugin:event|unlisten",{event:e,eventId:t})}async function Pe(e,t,n){return P("plugin:event|listen",{event:e,windowLabel:n==null?void 0:n.target,handler:fe(t)}).then(i=>async()=>gt(e,i))}async function zt(e,t,n){return Pe(e,i=>{t(i),gt(e,i.id).catch(()=>{})},n)}async function _t(e,t,n){await P("plugin:event|emit",{event:e,windowLabel:n==null?void 0:n.target,payload:t})}function Vt(e){let t,n,i,r,a,p,s,u;return{c(){t=f("div"),n=f("button"),n.textContent="Call Log API",i=g(),r=f("button"),r.textContent="Call Request (async) API",a=g(),p=f("button"),p.textContent="Send event to Rust",c(n,"class","btn"),c(n,"id","log"),c(r,"class","btn"),c(r,"id","request"),c(p,"class","btn"),c(p,"id","event")},m(d,E){k(d,t,E),o(t,n),o(t,i),o(t,r),o(t,a),o(t,p),s||(u=[z(n,"click",e[0]),z(r,"click",e[1]),z(p,"click",e[2])],s=!0)},p:$,i:$,o:$,d(d){d&&w(t),s=!1,V(u)}}}function Bt(e,t,n){let{onMessage:i}=t,r;xe(async()=>{r=await Pe("rust-event",i)}),at(()=>{r&&r()});function a(){P("log_operation",{event:"tauri-click",payload:"this payload is optional because we used Option in Rust"})}function p(){P("perform_request",{endpoint:"dummy endpoint arg",body:{id:5,name:"test"}}).then(i).catch(i)}function s(){_t("js-event","this is the payload string")}return e.$$set=u=>{"onMessage"in u&&n(3,i=u.onMessage)},[a,p,s,i]}class Gt extends Oe{constructor(t){super(),Se(this,t,Bt,Vt,he,{onMessage:3})}}function Xt(e){let t;return{c(){t=f("div"),t.innerHTML=`
Not available for Linux
+ `,c(t,"class","flex flex-col gap-2")},m(n,i){k(n,t,i)},p:$,i:$,o:$,d(n){n&&w(t)}}}function Yt(e,t,n){let{onMessage:i}=t;const r=window.constraints={audio:!0,video:!0};function a(s){const u=document.querySelector("video"),d=s.getVideoTracks();i("Got stream with constraints:",r),i(`Using video device: ${d[0].label}`),window.stream=s,u.srcObject=s}function p(s){if(s.name==="ConstraintNotSatisfiedError"){const u=r.video;i(`The resolution ${u.width.exact}x${u.height.exact} px is not supported by your device.`)}else s.name==="PermissionDeniedError"&&i("Permissions have not been granted to use your camera and microphone, you need to allow the page access to your devices in order for the demo to work.");i(`getUserMedia error: ${s.name}`,s)}return xe(async()=>{try{const s=await navigator.mediaDevices.getUserMedia(r);a(s)}catch(s){p(s)}}),at(()=>{window.stream.getTracks().forEach(function(s){s.stop()})}),e.$$set=s=>{"onMessage"in s&&n(0,i=s.onMessage)},[i]}class Jt extends Oe{constructor(t){super(),Se(this,t,Yt,Xt,he,{onMessage:0})}}function et(e,t,n){const i=e.slice();return i[25]=t[n],i}function tt(e,t,n){const i=e.slice();return i[28]=t[n],i}function Kt(e){let t;return{c(){t=f("span"),c(t,"class","i-codicon-menu animate-duration-300ms animate-fade-in")},m(n,i){k(n,t,i)},d(n){n&&w(t)}}}function Qt(e){let t;return{c(){t=f("span"),c(t,"class","i-codicon-close animate-duration-300ms animate-fade-in")},m(n,i){k(n,t,i)},d(n){n&&w(t)}}}function Zt(e){let t,n;return{c(){t=Q(`Switch to Dark mode + `),n=f("div"),c(n,"class","i-ph-moon")},m(i,r){k(i,t,r),k(i,n,r)},d(i){i&&w(t),i&&w(n)}}}function en(e){let t,n;return{c(){t=Q(`Switch to Light mode + `),n=f("div"),c(n,"class","i-ph-sun")},m(i,r){k(i,t,r),k(i,n,r)},d(i){i&&w(t),i&&w(n)}}}function tn(e){let t,n,i,r,a=e[28].label+"",p,s,u,d;function E(){return e[14](e[28])}return{c(){t=f("a"),n=f("div"),i=g(),r=f("p"),p=Q(a),c(n,"class",e[28].icon+" mr-2"),c(t,"href","##"),c(t,"class",s="nv "+(e[1]===e[28]?"nv_selected":""))},m(v,S){k(v,t,S),o(t,n),o(t,i),o(t,r),o(r,p),u||(d=z(t,"click",E),u=!0)},p(v,S){e=v,S&2&&s!==(s="nv "+(e[1]===e[28]?"nv_selected":""))&&c(t,"class",s)},d(v){v&&w(t),u=!1,d()}}}function nt(e){let t,n=e[28]&&tn(e);return{c(){n&&n.c(),t=lt()},m(i,r){n&&n.m(i,r),k(i,t,r)},p(i,r){i[28]&&n.p(i,r)},d(i){n&&n.d(i),i&&w(t)}}}function it(e){let t,n=e[25].html+"",i;return{c(){t=new xt(!1),i=lt(),t.a=i},m(r,a){t.m(n,r,a),k(r,i,a)},p(r,a){a&16&&n!==(n=r[25].html+"")&&t.p(n)},d(r){r&&w(i),r&&t.d()}}}function nn(e){let t,n,i,r,a,p,s,u,d,E,v,S,H,O,Z,I,me,b,j,D,q,B,ee,te,pe,ge,m,_,C,W,A,ne,U=e[1].label+"",Te,He,_e,ie,y,je,N,ve,qe,G,be,Ue,re,Fe,oe,se,De,ze;function Ve(l,T){return l[0]?Qt:Kt}let ye=Ve(e),M=ye(e);function Be(l,T){return l[2]?en:Zt}let we=Be(e),R=we(e),X=e[5],L=[];for(let l=0;l`,me=g(),b=f("a"),b.innerHTML=`GitHub - `,j=g(),C=f("a"),C.innerHTML=`Source - `,q=g(),B=f("br"),ee=g(),te=f("div"),pe=g(),ge=f("br"),p=g(),_=f("div");for(let s=0;s',ze=g(),oe=f("div");for(let s=0;s{Re(h,1)}),Dt()}Y?(y=new Y(Ge(s)),Qe(y.$$.fragment),Ae(y.$$.fragment,1),Me(y,ie,null)):y=null}if(T&16){J=s[4];let h;for(h=0;h{n(2,u=localStorage&&localStorage.getItem("theme")=="dark"),ot(u)});function d(){n(2,u=!u),ot(u)}let E=It([]);kt(e,E,p=>n(4,i=p));function v(p){E.update(_=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof p=="string"?p:JSON.stringify(p,null,1))+"
"},..._])}function S(p){E.update(_=>[{html:`
[${new Date().toLocaleTimeString()}]: `+p+"
"},..._])}function H(){E.update(()=>[])}let O,Z,I;function me(p){I=p.clientY;const _=window.getComputedStyle(O);Z=parseInt(_.height,10);const D=A=>{const ne=A.clientY-I,U=Z-ne;n(3,O.style.height=`${U{document.removeEventListener("mouseup",W),document.removeEventListener("mousemove",D)};document.addEventListener("mouseup",W),document.addEventListener("mousemove",D)}let b=!1,j,C,q=!1,B=0,ee=0;const te=(p,_,D)=>Math.min(Math.max(_,p),D);xe(()=>{n(13,j=document.querySelector("#sidebar")),C=document.querySelector("#sidebarToggle"),document.addEventListener("click",p=>{C.contains(p.target)?n(0,b=!b):b&&!j.contains(p.target)&&n(0,b=!1)}),document.addEventListener("touchstart",p=>{if(C.contains(p.target))return;const _=p.touches[0].clientX;(0<_&&_<20&&!b||b)&&(q=!0,B=_)}),document.addEventListener("touchmove",p=>{if(q){const _=p.touches[0].clientX;ee=_;const D=(_-B)/10;j.style.setProperty("--translate-x",`-${te(0,b?0-D:18.75-D,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(q){const p=(ee-B)/10;n(0,b=b?p>-(18.75/2):p>18.75/2)}q=!1})});const pe=p=>{c(p),n(0,b=!1)};function ge(p){Ne[p?"unshift":"push"](()=>{O=p,n(3,O)})}return e.$$.update=()=>{if(e.$$.dirty&1){const p=document.querySelector("#sidebar");p&&rn(p,b)}},[b,m,u,O,i,a,c,d,E,v,S,H,me,j,pe,ge]}class sn extends Oe{constructor(t){super(),Se(this,t,on,nn,he,{})}}new sn({target:document.querySelector("#app")}); + `,j=g(),D=f("a"),D.innerHTML=`Source + `,q=g(),B=f("br"),ee=g(),te=f("div"),pe=g(),ge=f("br"),m=g(),_=f("div");for(let l=0;l',Fe=g(),oe=f("div");for(let l=0;l{Re(h,1)}),Ct()}Y?(y=new Y(Ge(l)),Qe(y.$$.fragment),Ae(y.$$.fragment,1),Me(y,ie,null)):y=null}if(T&16){J=l[4];let h;for(h=0;h{n(2,u=localStorage&&localStorage.getItem("theme")=="dark"),ot(u)});function d(){n(2,u=!u),ot(u)}let E=It([]);kt(e,E,m=>n(4,i=m));function v(m){E.update(_=>[{html:`
[${new Date().toLocaleTimeString()}]: `+(typeof m=="string"?m:JSON.stringify(m,null,1))+"
"},..._])}function S(m){E.update(_=>[{html:`
[${new Date().toLocaleTimeString()}]: `+m+"
"},..._])}function H(){E.update(()=>[])}let O,Z,I;function me(m){I=m.clientY;const _=window.getComputedStyle(O);Z=parseInt(_.height,10);const C=A=>{const ne=A.clientY-I,U=Z-ne;n(3,O.style.height=`${U{document.removeEventListener("mouseup",W),document.removeEventListener("mousemove",C)};document.addEventListener("mouseup",W),document.addEventListener("mousemove",C)}let b=!1,j,D,q=!1,B=0,ee=0;const te=(m,_,C)=>Math.min(Math.max(_,m),C);xe(()=>{n(13,j=document.querySelector("#sidebar")),D=document.querySelector("#sidebarToggle"),document.addEventListener("click",m=>{D.contains(m.target)?n(0,b=!b):b&&!j.contains(m.target)&&n(0,b=!1)}),document.addEventListener("touchstart",m=>{if(D.contains(m.target))return;const _=m.touches[0].clientX;(0<_&&_<20&&!b||b)&&(q=!0,B=_)}),document.addEventListener("touchmove",m=>{if(q){const _=m.touches[0].clientX;ee=_;const C=(_-B)/10;j.style.setProperty("--translate-x",`-${te(0,b?0-C:18.75-C,18.75)}rem`)}}),document.addEventListener("touchend",()=>{if(q){const m=(ee-B)/10;n(0,b=b?m>-(18.75/2):m>18.75/2)}q=!1})});const pe=m=>{s(m),n(0,b=!1)};function ge(m){Ne[m?"unshift":"push"](()=>{O=m,n(3,O)})}return e.$$.update=()=>{if(e.$$.dirty&1){const m=document.querySelector("#sidebar");m&&rn(m,b)}},[b,p,u,O,i,a,s,d,E,v,S,H,me,j,pe,ge]}class sn extends Oe{constructor(t){super(),Se(this,t,on,nn,he,{})}}new sn({target:document.querySelector("#app")}); diff --git a/examples/api/isolation-dist/index.js b/examples/api/isolation-dist/index.js index 7e2df30de85d..84fc82e56088 100644 --- a/examples/api/isolation-dist/index.js +++ b/examples/api/isolation-dist/index.js @@ -2,6 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -window.__TAURI_ISOLATION_HOOK__ = (payload) => { +window.__TAURI_ISOLATION_HOOK__ = (payload, options) => { return payload } diff --git a/examples/api/src-tauri/Cargo.lock b/examples/api/src-tauri/Cargo.lock index 99fe15a24999..c6eac0884eb8 100644 --- a/examples/api/src-tauri/Cargo.lock +++ b/examples/api/src-tauri/Cargo.lock @@ -3243,9 +3243,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87728a671df8520c274fa9bed48d7384f5a965ef2fc364f01a942f6ff1ae6d2" +checksum = "436fb014010f6c87561125b14add8a6091354681f190bb9ffeb42819af9218a4" dependencies = [ "bitflags", "cairo-rs", @@ -3329,6 +3329,7 @@ dependencies = [ "jni", "libc", "log", + "mime", "objc", "once_cell", "percent-encoding", diff --git a/examples/api/src-tauri/src/cmd.rs b/examples/api/src-tauri/src/cmd.rs index 221881fc1b3b..e013e50a3add 100644 --- a/examples/api/src-tauri/src/cmd.rs +++ b/examples/api/src-tauri/src/cmd.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use tauri::command; #[derive(Debug, Deserialize)] @@ -17,8 +17,15 @@ pub fn log_operation(event: String, payload: Option) { log::info!("{} {:?}", event, payload); } +#[derive(Serialize)] +pub struct ApiResponse { + message: String, +} + #[command] -pub fn perform_request(endpoint: String, body: RequestBody) -> String { +pub fn perform_request(endpoint: String, body: RequestBody) -> ApiResponse { println!("{} {:?}", endpoint, body); - "message response".into() + ApiResponse { + message: "message response".into(), + } } diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs index 13e428037e37..0cb1a5a629d2 100644 --- a/examples/api/src-tauri/src/lib.rs +++ b/examples/api/src-tauri/src/lib.rs @@ -12,7 +12,7 @@ mod cmd; mod tray; use serde::Serialize; -use tauri::{window::WindowBuilder, App, AppHandle, RunEvent, Runtime, WindowUrl}; +use tauri::{ipc::Channel, window::WindowBuilder, App, AppHandle, RunEvent, Runtime, WindowUrl}; use tauri_plugin_sample::{PingRequest, SampleExt}; #[derive(Clone, Serialize)] @@ -66,6 +66,10 @@ pub fn run_app) + Send + 'static>( let value = Some("test".to_string()); let response = app.sample().ping(PingRequest { value: value.clone(), + on_event: Channel::new(|event| { + println!("got channel event: {:?}", event); + Ok(()) + }), }); log::info!("got response: {:?}", response); if let Ok(res) = response { diff --git a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt index 74ee8396e179..69f11898eac5 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt +++ b/examples/api/src-tauri/tauri-plugin-sample/android/src/main/java/com/plugin/sample/ExamplePlugin.kt @@ -17,6 +17,11 @@ class ExamplePlugin(private val activity: Activity): Plugin(activity) { @Command fun ping(invoke: Invoke) { + val onEvent = invoke.getChannel("onEvent") + val event = JSObject() + event.put("kind", "ping") + onEvent?.send(event) + val value = invoke.getString("value") ?: "" val ret = JSObject() ret.put("value", implementation.pong(value)) diff --git a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift index 8837a5a7be84..9774ba895dd0 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift +++ b/examples/api/src-tauri/tauri-plugin-sample/ios/Sources/ExamplePlugin.swift @@ -2,19 +2,22 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +import SwiftRs +import Tauri import UIKit import WebKit -import Tauri -import SwiftRs class ExamplePlugin: Plugin { - @objc public func ping(_ invoke: Invoke) throws { - let value = invoke.getString("value") - invoke.resolve(["value": value as Any]) - } + @objc public func ping(_ invoke: Invoke) throws { + let onEvent = invoke.getChannel("onEvent") + onEvent?.send(["kind": "ping"]) + + let value = invoke.getString("value") + invoke.resolve(["value": value as Any]) + } } @_cdecl("init_plugin_sample") func initPlugin() -> Plugin { - return ExamplePlugin() + return ExamplePlugin() } diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs b/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs index a0b975f776c4..b96655d6ce39 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/desktop.rs @@ -17,8 +17,14 @@ pub fn init( /// A helper class to access the sample APIs. pub struct Sample(AppHandle); +#[derive(serde::Serialize)] +struct Event { + kind: &'static str, +} + impl Sample { pub fn ping(&self, payload: PingRequest) -> crate::Result { + let _ = payload.on_event.send(Event { kind: "ping" }); Ok(PingResponse { value: payload.value, }) diff --git a/examples/api/src-tauri/tauri-plugin-sample/src/models.rs b/examples/api/src-tauri/tauri-plugin-sample/src/models.rs index e2ea913d36d1..3c824a58912c 100644 --- a/examples/api/src-tauri/tauri-plugin-sample/src/models.rs +++ b/examples/api/src-tauri/tauri-plugin-sample/src/models.rs @@ -3,10 +3,13 @@ // SPDX-License-Identifier: MIT use serde::{Deserialize, Serialize}; +use tauri::ipc::Channel; -#[derive(Debug, Serialize)] +#[derive(Serialize)] pub struct PingRequest { pub value: Option, + #[serde(rename = "onEvent")] + pub on_event: Channel, } #[derive(Debug, Clone, Default, Deserialize)] diff --git a/examples/api/src-tauri/tauri.conf.json b/examples/api/src-tauri/tauri.conf.json index fc7eef0ed0fb..74c601116bea 100644 --- a/examples/api/src-tauri/tauri.conf.json +++ b/examples/api/src-tauri/tauri.conf.json @@ -26,7 +26,11 @@ "name": "theme", "takesValue": true, "description": "App theme", - "possibleValues": ["light", "dark", "system"] + "possibleValues": [ + "light", + "dark", + "system" + ] }, { "short": "v", @@ -88,7 +92,10 @@ "security": { "csp": { "default-src": "'self' customprotocol: asset:", - "font-src": ["https://fonts.gstatic.com"], + "connect-src": "ipc: https://ipc.localhost", + "font-src": [ + "https://fonts.gstatic.com" + ], "img-src": "'self' asset: https://asset.localhost blob: data:", "style-src": "'unsafe-inline' 'self' https://fonts.googleapis.com" }, @@ -96,8 +103,13 @@ "assetProtocol": { "enable": true, "scope": { - "allow": ["$APPDATA/db/**", "$RESOURCE/**"], - "deny": ["$APPDATA/db/*.stronghold"] + "allow": [ + "$APPDATA/db/**", + "$RESOURCE/**" + ], + "deny": [ + "$APPDATA/db/*.stronghold" + ] } } }, diff --git a/examples/commands/index.html b/examples/commands/index.html index 35a307593db6..3abb8cb8a7e7 100644 --- a/examples/commands/index.html +++ b/examples/commands/index.html @@ -18,7 +18,8 @@

Tauri Commands

window.__TAURI__ .invoke(commandName, args) .then((response) => { - result.innerText = `Ok(${response})` + const val = response instanceof ArrayBuffer ? new TextDecoder().decode(response) : response + result.innerText = `Ok(${val})` }) .catch((error) => { result.innerText = `Err(${error})` @@ -28,6 +29,7 @@

Tauri Commands

const container = document.querySelector('#container') const commands = [ { name: 'borrow_cmd' }, + { name: 'raw_request' }, { name: 'window_label' }, { name: 'simple_command' }, { name: 'stateful_command' }, diff --git a/examples/commands/main.rs b/examples/commands/main.rs index 0ae6b58f183f..f9c1504b9dd5 100644 --- a/examples/commands/main.rs +++ b/examples/commands/main.rs @@ -9,7 +9,11 @@ mod commands; use commands::{cmd, invoke, message, resolver}; use serde::Deserialize; -use tauri::{command, State, Window}; +use tauri::{ + command, + ipc::{Request, Response}, + State, Window, +}; #[derive(Debug)] pub struct MyState { @@ -213,6 +217,12 @@ fn borrow_cmd_async(the_argument: &str) -> &str { the_argument } +#[command] +fn raw_request(request: Request<'_>) -> Response { + println!("{:?}", request); + Response::new(include_bytes!("./README.md").to_vec()) +} + fn main() { tauri::Builder::default() .manage(MyState { @@ -222,6 +232,7 @@ fn main() { .invoke_handler(tauri::generate_handler![ borrow_cmd, borrow_cmd_async, + raw_request, window_label, force_async, force_async_with_result, diff --git a/examples/commands/tauri.conf.json b/examples/commands/tauri.conf.json index 1393d40432ca..e618bd5472ab 100644 --- a/examples/commands/tauri.conf.json +++ b/examples/commands/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], + "distDir": [ + "index.html" + ], + "devPath": [ + "index.html" + ], "beforeDevCommand": "", "beforeBuildCommand": "", "withGlobalTauri": true @@ -47,7 +51,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/helloworld/tauri.conf.json b/examples/helloworld/tauri.conf.json index 90bd1e98a2fd..47869b636288 100644 --- a/examples/helloworld/tauri.conf.json +++ b/examples/helloworld/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], + "distDir": [ + "index.html" + ], + "devPath": [ + "index.html" + ], "beforeDevCommand": "", "beforeBuildCommand": "" }, @@ -46,7 +50,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/isolation/isolation-dist/index.js b/examples/isolation/isolation-dist/index.js index 260fe21d773b..680eae2b3e90 100644 --- a/examples/isolation/isolation-dist/index.js +++ b/examples/isolation/isolation-dist/index.js @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT -window.__TAURI_ISOLATION_HOOK__ = (payload) => { - console.log('hook', payload) +window.__TAURI_ISOLATION_HOOK__ = (payload, options) => { + console.log('hook', payload, options) return payload } diff --git a/examples/isolation/tauri.conf.json b/examples/isolation/tauri.conf.json index a93f342bfa1f..4187fb3c6036 100644 --- a/examples/isolation/tauri.conf.json +++ b/examples/isolation/tauri.conf.json @@ -60,7 +60,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/multiwindow/tauri.conf.json b/examples/multiwindow/tauri.conf.json index 3c0b34ad98b5..533ea9af644d 100644 --- a/examples/multiwindow/tauri.conf.json +++ b/examples/multiwindow/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], + "distDir": [ + "index.html" + ], + "devPath": [ + "index.html" + ], "withGlobalTauri": true }, "package": { @@ -43,7 +47,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/navigation/tauri.conf.json b/examples/navigation/tauri.conf.json index 97378e737391..c8a352404028 100644 --- a/examples/navigation/tauri.conf.json +++ b/examples/navigation/tauri.conf.json @@ -47,7 +47,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/parent-window/tauri.conf.json b/examples/parent-window/tauri.conf.json index 385eaae47181..b19c511f00c6 100644 --- a/examples/parent-window/tauri.conf.json +++ b/examples/parent-window/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], + "distDir": [ + "index.html" + ], + "devPath": [ + "index.html" + ], "withGlobalTauri": true }, "package": { @@ -27,7 +31,7 @@ "category": "DeveloperTool" }, "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/resources/src-tauri/tauri.conf.json b/examples/resources/src-tauri/tauri.conf.json index 7a94d46ab22a..85777f4273a3 100644 --- a/examples/resources/src-tauri/tauri.conf.json +++ b/examples/resources/src-tauri/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["../index.html"], - "devPath": ["../index.html"], + "distDir": [ + "../index.html" + ], + "devPath": [ + "../index.html" + ], "beforeDevCommand": "", "beforeBuildCommand": "", "withGlobalTauri": true @@ -23,7 +27,9 @@ "../../.icons/icon.icns", "../../.icons/icon.ico" ], - "resources": ["assets/*"], + "resources": [ + "assets/*" + ], "externalBin": [], "copyright": "", "category": "DeveloperTool", @@ -47,7 +53,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/splashscreen/tauri.conf.json b/examples/splashscreen/tauri.conf.json index d4e128ee7a69..69b17a36e3a3 100644 --- a/examples/splashscreen/tauri.conf.json +++ b/examples/splashscreen/tauri.conf.json @@ -44,7 +44,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/state/tauri.conf.json b/examples/state/tauri.conf.json index 4614753a1e96..d09175aaeb50 100644 --- a/examples/state/tauri.conf.json +++ b/examples/state/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["index.html"], - "devPath": ["index.html"], + "distDir": [ + "index.html" + ], + "devPath": [ + "index.html" + ], "beforeDevCommand": "", "beforeBuildCommand": "", "withGlobalTauri": true @@ -47,7 +51,7 @@ } ], "security": { - "csp": "default-src 'self'" + "csp": "default-src 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/examples/streaming/tauri.conf.json b/examples/streaming/tauri.conf.json index 0dfdb7ab3603..970f5760ebd5 100644 --- a/examples/streaming/tauri.conf.json +++ b/examples/streaming/tauri.conf.json @@ -47,7 +47,7 @@ } ], "security": { - "csp": "default-src 'self'; media-src stream: https://stream.localhost asset: https://asset.localhost", + "csp": "default-src 'self' ipc:; media-src stream: https://stream.localhost asset: https://asset.localhost", "assetProtocol": { "scope": ["**/test_video.mp4"] } diff --git a/examples/tauri-dynamic-lib/src-tauri/tauri.conf.json b/examples/tauri-dynamic-lib/src-tauri/tauri.conf.json index 2d54d1dcee1a..a7d9b6a090d7 100644 --- a/examples/tauri-dynamic-lib/src-tauri/tauri.conf.json +++ b/examples/tauri-dynamic-lib/src-tauri/tauri.conf.json @@ -1,8 +1,12 @@ { "$schema": "../../../core/tauri-config-schema/schema.json", "build": { - "distDir": ["src/index.html"], - "devPath": ["src/index.html"], + "distDir": [ + "src/index.html" + ], + "devPath": [ + "src/index.html" + ], "beforeDevCommand": "", "beforeBuildCommand": "" }, @@ -46,7 +50,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/tooling/api/docs/js-api.json b/tooling/api/docs/js-api.json index fcdc14dfa413..8429ed2cd95c 100644 --- a/tooling/api/docs/js-api.json +++ b/tooling/api/docs/js-api.json @@ -1 +1 @@ -{"id":0,"name":"@tauri-apps/api","variant":"project","kind":1,"flags":{},"children":[{"id":1,"name":"event","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"The event system allows you to emit events to the backend and listen to events from it.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.event`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":36,"name":"TauriEvent","variant":"declaration","kind":8,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.1.0"}]}]},"children":[{"id":49,"name":"MENU","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":59,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L59"}],"type":{"type":"literal","value":"tauri://menu"}},{"id":43,"name":"WINDOW_BLUR","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":53,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L53"}],"type":{"type":"literal","value":"tauri://blur"}},{"id":39,"name":"WINDOW_CLOSE_REQUESTED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":49,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L49"}],"type":{"type":"literal","value":"tauri://close-requested"}},{"id":40,"name":"WINDOW_CREATED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":50,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L50"}],"type":{"type":"literal","value":"tauri://window-created"}},{"id":41,"name":"WINDOW_DESTROYED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":51,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L51"}],"type":{"type":"literal","value":"tauri://destroyed"}},{"id":46,"name":"WINDOW_FILE_DROP","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":56,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L56"}],"type":{"type":"literal","value":"tauri://file-drop"}},{"id":48,"name":"WINDOW_FILE_DROP_CANCELLED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":58,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L58"}],"type":{"type":"literal","value":"tauri://file-drop-cancelled"}},{"id":47,"name":"WINDOW_FILE_DROP_HOVER","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":57,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L57"}],"type":{"type":"literal","value":"tauri://file-drop-hover"}},{"id":42,"name":"WINDOW_FOCUS","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":52,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L52"}],"type":{"type":"literal","value":"tauri://focus"}},{"id":38,"name":"WINDOW_MOVED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":48,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L48"}],"type":{"type":"literal","value":"tauri://move"}},{"id":37,"name":"WINDOW_RESIZED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":47,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L47"}],"type":{"type":"literal","value":"tauri://resize"}},{"id":44,"name":"WINDOW_SCALE_FACTOR_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":54,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L54"}],"type":{"type":"literal","value":"tauri://scale-change"}},{"id":45,"name":"WINDOW_THEME_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":55,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L55"}],"type":{"type":"literal","value":"tauri://theme-changed"}}],"groups":[{"title":"Enumeration Members","children":[49,43,39,40,41,46,48,47,42,38,37,44,45]}],"sources":[{"fileName":"event.ts","line":46,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L46"}]},{"id":2,"name":"Event","variant":"declaration","kind":256,"flags":{},"children":[{"id":3,"name":"event","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name"}]},"sources":[{"fileName":"event.ts","line":16,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L16"}],"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":5,"name":"id","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event identifier used to unlisten"}]},"sources":[{"fileName":"event.ts","line":20,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L20"}],"type":{"type":"intrinsic","name":"number"}},{"id":6,"name":"payload","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event payload"}]},"sources":[{"fileName":"event.ts","line":22,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L22"}],"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}},{"id":4,"name":"windowLabel","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"The label of the window that emitted this event."}]},"sources":[{"fileName":"event.ts","line":18,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L18"}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[3,5,6,4]}],"sources":[{"fileName":"event.ts","line":14,"character":10,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L14"}],"typeParameters":[{"id":7,"name":"T","variant":"typeParam","kind":131072,"flags":{}}]},{"id":17,"name":"Options","variant":"declaration","kind":256,"flags":{},"children":[{"id":18,"name":"target","variant":"declaration","kind":1024,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"Label of the window the function targets.\n\nWhen listening to events and using this value,\nonly events triggered by the window with the given label are received.\n\nWhen emitting events, only the window with the given label will receive it."}]},"sources":[{"fileName":"event.ts","line":40,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L40"}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[18]}],"sources":[{"fileName":"event.ts","line":31,"character":10,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L31"}]},{"id":8,"name":"EventCallback","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":25,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L25"}],"typeParameters":[{"id":12,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"type":{"type":"reflection","declaration":{"id":9,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"event.ts","line":25,"character":24,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L25"}],"signatures":[{"id":10,"name":"__type","variant":"signature","kind":4096,"flags":{},"parameters":[{"id":11,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":2,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Event","package":"@tauri-apps/api"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":16,"name":"EventName","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":29,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L29"}],"type":{"type":"union","types":[{"type":"templateLiteral","head":"","tail":[[{"type":"reference","target":36,"name":"TauriEvent","package":"@tauri-apps/api"},""]]},{"type":"intersection","types":[{"type":"intrinsic","name":"string"},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"never"},{"type":"intrinsic","name":"never"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}]}]}},{"id":13,"name":"UnlistenFn","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":27,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L27"}],"type":{"type":"reflection","declaration":{"id":14,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"event.ts","line":27,"character":18,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L27"}],"signatures":[{"id":15,"name":"__type","variant":"signature","kind":4096,"flags":{},"type":{"type":"intrinsic","name":"void"}}]}}},{"id":31,"name":"emit","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":164,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L164"}],"signatures":[{"id":32,"name":"emit","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Emits an event to the backend and all Tauri windows."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { emit } from '@tauri-apps/api/event';\nawait emit('frontend-loaded', { loggedIn: true, token: 'authToken' });\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":164,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L164"}],"parameters":[{"id":33,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"intrinsic","name":"string"}},{"id":34,"name":"payload","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"intrinsic","name":"unknown"}},{"id":35,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":19,"name":"listen","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":99,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L99"}],"signatures":[{"id":20,"name":"listen","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an event. The event can be either global or window-specific.\nSee "},{"kind":"inline-tag","tag":"@link","text":"windowLabel","target":4,"tsLinkText":""},{"kind":"text","text":" to check the event source."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { listen } from '@tauri-apps/api/event';\nconst unlisten = await listen('error', (event) => {\n console.log(`Got error in window ${event.windowLabel}, payload: ${event.payload}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":99,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L99"}],"typeParameter":[{"id":21,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":22,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":23,"name":"handler","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event handler callback."}]},"type":{"type":"reference","target":8,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}},{"id":24,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":13,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":25,"name":"once","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":137,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L137"}],"signatures":[{"id":26,"name":"once","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an one-off event. See "},{"kind":"inline-tag","tag":"@link","text":"listen","target":19,"tsLinkText":""},{"kind":"text","text":" for more information."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { once } from '@tauri-apps/api/event';\ninterface LoadedPayload {\n loggedIn: boolean,\n token: string\n}\nconst unlisten = await once('loaded', (event) => {\n console.log(`App is loaded, loggedIn: ${event.payload.loggedIn}, token: ${event.payload.token}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":137,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L137"}],"typeParameter":[{"id":27,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":28,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":29,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":8,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}},{"id":30,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":13,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Enumerations","children":[36]},{"title":"Interfaces","children":[2,17]},{"title":"Type Aliases","children":[8,16,13]},{"title":"Functions","children":[31,19,25]}],"sources":[{"fileName":"event.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/event.ts#L1"}]},{"id":50,"name":"mocks","variant":"declaration","kind":2,"flags":{},"children":[{"id":62,"name":"clearMocks","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":178,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L178"}],"signatures":[{"id":63,"name":"clearMocks","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Clears mocked functions/data injected by the other functions in this module.\nWhen using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.\n\n# Example\n\n"},{"kind":"code","text":"```js\nimport { mockWindows, clearMocks } from \"@tauri-apps/api/mocks\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked windows\", () => {\n mockWindows(\"main\", \"second\", \"third\");\n\n expect(window).toHaveProperty(\"__TAURI_METADATA__\")\n})\n\ntest(\"no mocked windows\", () => {\n expect(window).not.toHaveProperty(\"__TAURI_METADATA__\")\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":178,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L178"}],"type":{"type":"intrinsic","name":"void"}}]},{"id":51,"name":"mockIPC","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":80,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L80"}],"signatures":[{"id":52,"name":"mockIPC","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Intercepts all IPC requests with the given mock handler.\n\nThis function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.\n\n# Examples\n\nTesting setup using vitest:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n switch (cmd) {\n case \"add\":\n return (args.a as number) + (args.b as number);\n default:\n break;\n }\n });\n\n expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);\n})\n```"},{"kind":"text","text":"\n\nThe callback function can also return a Promise:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, args) => {\n if(cmd === \"get_data\") {\n return fetch(\"https://example.com/data.json\")\n .then((response) => response.json())\n }\n });\n\n expect(invoke('get_data')).resolves.toBe({ foo: 'bar' });\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":80,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L80"}],"parameters":[{"id":53,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":54,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L81"}],"signatures":[{"id":55,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L81"}],"parameters":[{"id":56,"name":"cmd","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":57,"name":"args","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}}],"type":{"type":"intrinsic","name":"any"}}]}}}],"type":{"type":"intrinsic","name":"void"}}]},{"id":58,"name":"mockWindows","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":142,"character":16,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L142"}],"signatures":[{"id":59,"name":"mockWindows","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Mocks one or many window labels.\nIn non-tauri context it is required to call this function *before* using the "},{"kind":"code","text":"`@tauri-apps/api/window`"},{"kind":"text","text":" module.\n\nThis function only mocks the *presence* of windows,\nwindow properties (e.g. width and height) can be mocked like regular IPC calls using the "},{"kind":"code","text":"`mockIPC`"},{"kind":"text","text":" function.\n\n# Examples\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\nimport { getCurrent } from \"@tauri-apps/api/window\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nconst win = getCurrent();\n\nwin.label // \"main\"\n```"},{"kind":"text","text":"\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nmockIPC((cmd, args) => {\n if (cmd === \"plugin:event|emit\") {\n console.log('emit event', args?.event, args?.payload);\n }\n});\n\nconst { emit } = await import(\"@tauri-apps/api/event\");\nawait emit('loaded'); // this will cause the mocked IPC handler to log to the console.\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":142,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L142"}],"parameters":[{"id":60,"name":"current","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Label of window this JavaScript context is running in."}]},"type":{"type":"intrinsic","name":"string"}},{"id":61,"name":"additionalWindows","variant":"param","kind":32768,"flags":{"isRest":true},"comment":{"summary":[{"kind":"text","text":"Label of additional windows the app has."}]},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"intrinsic","name":"void"}}]}],"groups":[{"title":"Functions","children":[62,51,58]}],"sources":[{"fileName":"mocks.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/mocks.ts#L1"}]},{"id":64,"name":"path","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"The path module provides utilities for working with file and directory paths.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.path`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":".\n\nIt is recommended to allowlist only the APIs you use for optimal bundle size and security."}]},"children":[{"id":65,"name":"BaseDirectory","variant":"declaration","kind":8,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"children":[{"id":81,"name":"AppCache","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":35,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L35"}],"type":{"type":"literal","value":16}},{"id":78,"name":"AppConfig","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":32,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L32"}],"type":{"type":"literal","value":13}},{"id":79,"name":"AppData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":33,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L33"}],"type":{"type":"literal","value":14}},{"id":80,"name":"AppLocalData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":34,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L34"}],"type":{"type":"literal","value":15}},{"id":82,"name":"AppLog","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":36,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L36"}],"type":{"type":"literal","value":17}},{"id":66,"name":"Audio","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":20,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L20"}],"type":{"type":"literal","value":1}},{"id":67,"name":"Cache","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":21,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L21"}],"type":{"type":"literal","value":2}},{"id":68,"name":"Config","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":22,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L22"}],"type":{"type":"literal","value":3}},{"id":69,"name":"Data","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":23,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L23"}],"type":{"type":"literal","value":4}},{"id":83,"name":"Desktop","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":38,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L38"}],"type":{"type":"literal","value":18}},{"id":71,"name":"Document","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":25,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L25"}],"type":{"type":"literal","value":6}},{"id":72,"name":"Download","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":26,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L26"}],"type":{"type":"literal","value":7}},{"id":84,"name":"Executable","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":39,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L39"}],"type":{"type":"literal","value":19}},{"id":85,"name":"Font","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":40,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L40"}],"type":{"type":"literal","value":20}},{"id":86,"name":"Home","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":41,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L41"}],"type":{"type":"literal","value":21}},{"id":70,"name":"LocalData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":24,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L24"}],"type":{"type":"literal","value":5}},{"id":73,"name":"Picture","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":27,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L27"}],"type":{"type":"literal","value":8}},{"id":74,"name":"Public","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":28,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L28"}],"type":{"type":"literal","value":9}},{"id":76,"name":"Resource","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":30,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L30"}],"type":{"type":"literal","value":11}},{"id":87,"name":"Runtime","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":42,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L42"}],"type":{"type":"literal","value":22}},{"id":77,"name":"Temp","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":31,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L31"}],"type":{"type":"literal","value":12}},{"id":88,"name":"Template","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":43,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L43"}],"type":{"type":"literal","value":23}},{"id":75,"name":"Video","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":29,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L29"}],"type":{"type":"literal","value":10}}],"groups":[{"title":"Enumeration Members","children":[81,78,79,80,82,66,67,68,69,83,71,72,84,85,86,70,73,74,76,87,77,88,75]}],"sources":[{"fileName":"path.ts","line":19,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L19"}]},{"id":95,"name":"appCacheDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":119,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L119"}],"signatures":[{"id":96,"name":"appCacheDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's cache files.\nResolves to "},{"kind":"code","text":"`${cacheDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appCacheDir } from '@tauri-apps/api/path';\nconst appCacheDirPath = await appCacheDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":119,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L119"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":89,"name":"appConfigDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":68,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L68"}],"signatures":[{"id":90,"name":"appConfigDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's config files.\nResolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appConfigDir } from '@tauri-apps/api/path';\nconst appConfigDirPath = await appConfigDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":68,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L68"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":91,"name":"appDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":85,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L85"}],"signatures":[{"id":92,"name":"appDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's data files.\nResolves to "},{"kind":"code","text":"`${dataDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":85,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L85"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":93,"name":"appLocalDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":102,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L102"}],"signatures":[{"id":94,"name":"appLocalDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's local data files.\nResolves to "},{"kind":"code","text":"`${localDataDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appLocalDataDir } from '@tauri-apps/api/path';\nconst appLocalDataDirPath = await appLocalDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":102,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L102"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":97,"name":"appLogDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":531,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L531"}],"signatures":[{"id":98,"name":"appLogDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's log files.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}/logs`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`${homeDir}/Library/Logs/{bundleIdentifier}`"},{"kind":"text","text":"\n- **Windows:** Resolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}/logs`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appLogDir } from '@tauri-apps/api/path';\nconst appLogDirPath = await appLogDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":531,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L531"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":99,"name":"audioDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":141,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L141"}],"signatures":[{"id":100,"name":"audioDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's audio directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_MUSIC_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Music`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Music}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { audioDir } from '@tauri-apps/api/path';\nconst audioDirPath = await audioDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":141,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L141"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":155,"name":"basename","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":664,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L664"}],"signatures":[{"id":156,"name":"basename","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the last portion of a "},{"kind":"code","text":"`path`"},{"kind":"text","text":". Trailing directory separators are ignored."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { basename, resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('app.conf');\nconst base = await basename(resourcePath);\nassert(base === 'app.conf');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":664,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L664"}],"parameters":[{"id":157,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":158,"name":"ext","variant":"param","kind":32768,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"An optional file extension to be removed from the returned path."}]},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":101,"name":"cacheDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":163,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L163"}],"signatures":[{"id":102,"name":"cacheDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's cache directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_CACHE_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.cache`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Caches`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_LocalAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { cacheDir } from '@tauri-apps/api/path';\nconst cacheDirPath = await cacheDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":163,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L163"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":103,"name":"configDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":185,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L185"}],"signatures":[{"id":104,"name":"configDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's config directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_CONFIG_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.config`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_RoamingAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { configDir } from '@tauri-apps/api/path';\nconst configDirPath = await configDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":185,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L185"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":105,"name":"dataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":207,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L207"}],"signatures":[{"id":106,"name":"dataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's data directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_RoamingAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { dataDir } from '@tauri-apps/api/path';\nconst dataDirPath = await dataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":207,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L207"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":138,"name":"delimiter","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":571,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L571"}],"signatures":[{"id":139,"name":"delimiter","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the platform-specific path segment delimiter:\n- "},{"kind":"code","text":"`;`"},{"kind":"text","text":" on Windows\n- "},{"kind":"code","text":"`:`"},{"kind":"text","text":" on POSIX"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":571,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L571"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":107,"name":"desktopDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":229,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L229"}],"signatures":[{"id":108,"name":"desktopDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's desktop directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DESKTOP_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Desktop`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Desktop}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { desktopDir } from '@tauri-apps/api/path';\nconst desktopPath = await desktopDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":229,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L229"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":149,"name":"dirname","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":630,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L630"}],"signatures":[{"id":150,"name":"dirname","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the directory name of a "},{"kind":"code","text":"`path`"},{"kind":"text","text":". Trailing directory separators are ignored."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { dirname, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst dir = await dirname(appDataDirPath);\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":630,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L630"}],"parameters":[{"id":151,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":109,"name":"documentDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":251,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L251"}],"signatures":[{"id":110,"name":"documentDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's document directory."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { documentDir } from '@tauri-apps/api/path';\nconst documentDirPath = await documentDir();\n```"},{"kind":"text","text":"\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DOCUMENTS_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Documents`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Documents}`"},{"kind":"text","text":"."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":251,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L251"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":111,"name":"downloadDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":273,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L273"}],"signatures":[{"id":112,"name":"downloadDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's download directory.\n\n#### Platform-specific\n\n- **Linux**: Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DOWNLOAD_DIR`"},{"kind":"text","text":".\n- **macOS**: Resolves to "},{"kind":"code","text":"`$HOME/Downloads`"},{"kind":"text","text":".\n- **Windows**: Resolves to "},{"kind":"code","text":"`{FOLDERID_Downloads}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { downloadDir } from '@tauri-apps/api/path';\nconst downloadDirPath = await downloadDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":273,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L273"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":113,"name":"executableDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":295,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L295"}],"signatures":[{"id":114,"name":"executableDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's executable directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_BIN_HOME/../bin`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$XDG_DATA_HOME/../bin`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/bin`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { executableDir } from '@tauri-apps/api/path';\nconst executableDirPath = await executableDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":295,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L295"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":152,"name":"extname","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":646,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L646"}],"signatures":[{"id":153,"name":"extname","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the extension of the "},{"kind":"code","text":"`path`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { extname, resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('app.conf');\nconst ext = await extname(resourcePath);\nassert(ext === 'conf');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":646,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L646"}],"parameters":[{"id":154,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":115,"name":"fontDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":317,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L317"}],"signatures":[{"id":116,"name":"fontDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's font directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME/fonts`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share/fonts`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Fonts`"},{"kind":"text","text":".\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { fontDir } from '@tauri-apps/api/path';\nconst fontDirPath = await fontDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":317,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L317"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":117,"name":"homeDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":339,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L339"}],"signatures":[{"id":118,"name":"homeDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's home directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$HOME`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Profile}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { homeDir } from '@tauri-apps/api/path';\nconst homeDirPath = await homeDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":339,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L339"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":159,"name":"isAbsolute","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":678,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L678"}],"signatures":[{"id":160,"name":"isAbsolute","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns whether the path is absolute or not."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { isAbsolute } from '@tauri-apps/api/path';\nassert(await isAbsolute('/home/tauri'));\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":678,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L678"}],"parameters":[{"id":161,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":146,"name":"join","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":615,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L615"}],"signatures":[{"id":147,"name":"join","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Joins all given "},{"kind":"code","text":"`path`"},{"kind":"text","text":" segments together using the platform-specific separator as a delimiter, then normalizes the resulting path."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { join, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await join(appDataDirPath, 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":615,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L615"}],"parameters":[{"id":148,"name":"paths","variant":"param","kind":32768,"flags":{"isRest":true},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":119,"name":"localDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":361,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L361"}],"signatures":[{"id":120,"name":"localDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's local data directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_LocalAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { localDataDir } from '@tauri-apps/api/path';\nconst localDataDirPath = await localDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":361,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L361"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":143,"name":"normalize","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":600,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L600"}],"signatures":[{"id":144,"name":"normalize","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Normalizes the given "},{"kind":"code","text":"`path`"},{"kind":"text","text":", resolving "},{"kind":"code","text":"`'..'`"},{"kind":"text","text":" and "},{"kind":"code","text":"`'.'`"},{"kind":"text","text":" segments and resolve symbolic links."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { normalize, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await normalize(appDataDirPath, '..', 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":600,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L600"}],"parameters":[{"id":145,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":121,"name":"pictureDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":383,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L383"}],"signatures":[{"id":122,"name":"pictureDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's picture directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_PICTURES_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Pictures`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Pictures}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { pictureDir } from '@tauri-apps/api/path';\nconst pictureDirPath = await pictureDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":383,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L383"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":123,"name":"publicDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":405,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L405"}],"signatures":[{"id":124,"name":"publicDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's public directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_PUBLICSHARE_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Public`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Public}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { publicDir } from '@tauri-apps/api/path';\nconst publicDirPath = await publicDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":405,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L405"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":140,"name":"resolve","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":585,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L585"}],"signatures":[{"id":141,"name":"resolve","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Resolves a sequence of "},{"kind":"code","text":"`paths`"},{"kind":"text","text":" or "},{"kind":"code","text":"`path`"},{"kind":"text","text":" segments into an absolute path."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resolve, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await resolve(appDataDirPath, '..', 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":585,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L585"}],"parameters":[{"id":142,"name":"paths","variant":"param","kind":32768,"flags":{"isRest":true},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":127,"name":"resolveResource","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":442,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L442"}],"signatures":[{"id":128,"name":"resolveResource","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Resolve the path to a resource file."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('script.sh');\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"The full path to the resource."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":442,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L442"}],"parameters":[{"id":129,"name":"resourcePath","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The path to the resource.\nMust follow the same syntax as defined in "},{"kind":"code","text":"`tauri.conf.json > tauri > bundle > resources`"},{"kind":"text","text":", i.e. keeping subfolders and parent dir components ("},{"kind":"code","text":"`../`"},{"kind":"text","text":")."}]},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":125,"name":"resourceDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":422,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L422"}],"signatures":[{"id":126,"name":"resourceDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the application's resource directory.\nTo resolve a resource path, see the [[resolveResource | "},{"kind":"code","text":"`resolveResource API`"},{"kind":"text","text":"]]."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resourceDir } from '@tauri-apps/api/path';\nconst resourceDirPath = await resourceDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":422,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L422"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":130,"name":"runtimeDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":465,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L465"}],"signatures":[{"id":131,"name":"runtimeDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's runtime directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_RUNTIME_DIR`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { runtimeDir } from '@tauri-apps/api/path';\nconst runtimeDirPath = await runtimeDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":465,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L465"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":136,"name":"sep","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":560,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L560"}],"signatures":[{"id":137,"name":"sep","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the platform-specific path segment separator:\n- "},{"kind":"code","text":"`\\` on Windows\n- `"},{"kind":"text","text":"/` on POSIX"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":560,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L560"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":162,"name":"tempDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":547,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L547"}],"signatures":[{"id":163,"name":"tempDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns a temporary directory."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { tempDir } from '@tauri-apps/api/path';\nconst temp = await tempDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":547,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L547"}],"parameters":[{"id":164,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":132,"name":"templateDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":487,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L487"}],"signatures":[{"id":133,"name":"templateDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's template directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_TEMPLATES_DIR`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Templates}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { templateDir } from '@tauri-apps/api/path';\nconst templateDirPath = await templateDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":487,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L487"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":134,"name":"videoDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":509,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L509"}],"signatures":[{"id":135,"name":"videoDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's video directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_VIDEOS_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Movies`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Videos}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { videoDir } from '@tauri-apps/api/path';\nconst videoDirPath = await videoDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":509,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L509"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Enumerations","children":[65]},{"title":"Functions","children":[95,89,91,93,97,99,155,101,103,105,138,107,149,109,111,113,152,115,117,159,146,119,143,121,123,140,127,125,130,136,162,132,134]}],"sources":[{"fileName":"path.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/path.ts#L1"}]},{"id":165,"name":"tauri","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"Invoke your custom commands.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.tauri`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":174,"name":"Channel","variant":"declaration","kind":128,"flags":{},"children":[{"id":175,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":66,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L66"}],"signatures":[{"id":176,"name":"new Channel","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":66,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L66"}],"typeParameter":[{"id":177,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","target":174,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Channel","package":"@tauri-apps/api"}}]},{"id":180,"name":"#onmessage","variant":"declaration","kind":1024,"flags":{"isPrivate":true},"sources":[{"fileName":"tauri.ts","line":62,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L62"}],"type":{"type":"reflection","declaration":{"id":181,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":62,"character":14,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L62"}],"signatures":[{"id":182,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":62,"character":14,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L62"}],"parameters":[{"id":183,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}},"defaultValue":"..."},{"id":179,"name":"__TAURI_CHANNEL_MARKER__","variant":"declaration","kind":1024,"flags":{"isPrivate":true,"isReadonly":true},"sources":[{"fileName":"tauri.ts","line":61,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L61"}],"type":{"type":"literal","value":true},"defaultValue":"true"},{"id":178,"name":"id","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":59,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L59"}],"type":{"type":"intrinsic","name":"number"}},{"id":184,"name":"onmessage","variant":"declaration","kind":262144,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L72"},{"fileName":"tauri.ts","line":76,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L76"}],"getSignature":{"id":185,"name":"onmessage","variant":"signature","kind":524288,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L76"}],"type":{"type":"reflection","declaration":{"id":186,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L76"}],"signatures":[{"id":187,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":76,"character":19,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L76"}],"parameters":[{"id":188,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}},"setSignature":{"id":189,"name":"onmessage","variant":"signature","kind":1048576,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L72"}],"parameters":[{"id":190,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":191,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L72"}],"signatures":[{"id":192,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":72,"character":25,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L72"}],"parameters":[{"id":193,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"intrinsic","name":"void"}}},{"id":194,"name":"toJSON","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":80,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L80"}],"signatures":[{"id":195,"name":"toJSON","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":80,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L80"}],"type":{"type":"intrinsic","name":"string"}}]}],"groups":[{"title":"Constructors","children":[175]},{"title":"Properties","children":[180,179,178]},{"title":"Accessors","children":[184]},{"title":"Methods","children":[194]}],"sources":[{"fileName":"tauri.ts","line":58,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L58"}],"typeParameters":[{"id":196,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}]},{"id":197,"name":"PluginListener","variant":"declaration","kind":128,"flags":{},"children":[{"id":198,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":90,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L90"}],"signatures":[{"id":199,"name":"new PluginListener","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":90,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L90"}],"parameters":[{"id":200,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":201,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":202,"name":"channelId","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"number"}}],"type":{"type":"reference","target":197,"name":"PluginListener","package":"@tauri-apps/api"}}]},{"id":205,"name":"channelId","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":88,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L88"}],"type":{"type":"intrinsic","name":"number"}},{"id":204,"name":"event","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":87,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L87"}],"type":{"type":"intrinsic","name":"string"}},{"id":203,"name":"plugin","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":86,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L86"}],"type":{"type":"intrinsic","name":"string"}},{"id":206,"name":"unregister","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":96,"character":8,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L96"}],"signatures":[{"id":207,"name":"unregister","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":96,"character":2,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L96"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Constructors","children":[198]},{"title":"Properties","children":[205,204,203]},{"title":"Methods","children":[206]}],"sources":[{"fileName":"tauri.ts","line":85,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L85"}]},{"id":166,"name":"InvokeArgs","variant":"declaration","kind":4194304,"flags":{},"comment":{"summary":[{"kind":"text","text":"Command arguments."}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":128,"character":5,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L128"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}},{"id":208,"name":"addPluginListener","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":111,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L111"}],"signatures":[{"id":209,"name":"addPluginListener","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Adds a listener to a plugin event."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"The listener object to stop listening to the events."}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":111,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L111"}],"typeParameter":[{"id":210,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":211,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":212,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":213,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":214,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":114,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L114"}],"signatures":[{"id":215,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":114,"character":6,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L114"}],"parameters":[{"id":216,"name":"payload","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":197,"name":"PluginListener","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":222,"name":"convertFileSrc","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":194,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L194"}],"signatures":[{"id":223,"name":"convertFileSrc","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Convert a device file path to an URL that can be loaded by the webview.\nNote that "},{"kind":"code","text":"`asset:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`https://asset.localhost`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.security.csp`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#securityconfig.csp) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":".\nExample CSP value: "},{"kind":"code","text":"`\"csp\": \"default-src 'self'; img-src 'self' asset: https://asset.localhost\"`"},{"kind":"text","text":" to use the asset protocol on image sources.\n\nAdditionally, "},{"kind":"code","text":"`asset`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.allowlist.protocol`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#allowlistconfig.protocol)\nin "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" and its access scope must be defined on the "},{"kind":"code","text":"`assetScope`"},{"kind":"text","text":" array on the same "},{"kind":"code","text":"`protocol`"},{"kind":"text","text":" object."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir, join } from '@tauri-apps/api/path';\nimport { convertFileSrc } from '@tauri-apps/api/tauri';\nconst appDataDirPath = await appDataDir();\nconst filePath = await join(appDataDirPath, 'assets/video.mp4');\nconst assetUrl = convertFileSrc(filePath);\n\nconst video = document.getElementById('my-video');\nconst source = document.createElement('source');\nsource.type = 'video/mp4';\nsource.src = assetUrl;\nvideo.appendChild(source);\nvideo.load();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"the URL that can be used as source on the webview."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":194,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L194"}],"parameters":[{"id":224,"name":"filePath","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The file path."}]},"type":{"type":"intrinsic","name":"string"}},{"id":225,"name":"protocol","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The protocol to use. Defaults to "},{"kind":"code","text":"`asset`"},{"kind":"text","text":". You only need to set this when using a custom protocol."}]},"type":{"type":"intrinsic","name":"string"},"defaultValue":"'asset'"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":217,"name":"invoke","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":144,"character":15,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L144"}],"signatures":[{"id":218,"name":"invoke","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Sends a message to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { invoke } from '@tauri-apps/api/tauri';\nawait invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving or rejecting to the backend response."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":144,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L144"}],"typeParameter":[{"id":219,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":220,"name":"cmd","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The command name."}]},"type":{"type":"intrinsic","name":"string"}},{"id":221,"name":"args","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The optional arguments to pass to the command."}]},"type":{"type":"reference","target":166,"name":"InvokeArgs","package":"@tauri-apps/api"},"defaultValue":"{}"}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":167,"name":"transformCallback","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":36,"character":9,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L36"}],"signatures":[{"id":168,"name":"transformCallback","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Transforms a callback function to a string identifier that can be passed to the backend.\nThe backend uses the identifier to "},{"kind":"code","text":"`eval()`"},{"kind":"text","text":" the callback."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"A unique identifier associated with the callback function."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":36,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L36"}],"parameters":[{"id":169,"name":"callback","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reflection","declaration":{"id":170,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":37,"character":13,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L37"}],"signatures":[{"id":171,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":37,"character":13,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L37"}],"parameters":[{"id":172,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"any"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":173,"name":"once","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"intrinsic","name":"number"}}]}],"groups":[{"title":"Classes","children":[174,197]},{"title":"Type Aliases","children":[166]},{"title":"Functions","children":[208,222,217,167]}],"sources":[{"fileName":"tauri.ts","line":1,"character":0,"url":"https://github.com/tauri-apps/tauri/blob/a5752db9/tooling/api/src/tauri.ts#L1"}]}],"groups":[{"title":"Modules","children":[1,50,64,165]}],"packageName":"@tauri-apps/api","symbolIdMap":{"1":{"sourceFileName":"src/event.ts","qualifiedName":""},"2":{"sourceFileName":"src/event.ts","qualifiedName":"Event"},"3":{"sourceFileName":"src/event.ts","qualifiedName":"Event.event"},"4":{"sourceFileName":"src/event.ts","qualifiedName":"Event.windowLabel"},"5":{"sourceFileName":"src/event.ts","qualifiedName":"Event.id"},"6":{"sourceFileName":"src/event.ts","qualifiedName":"Event.payload"},"7":{"sourceFileName":"src/event.ts","qualifiedName":"Event.T"},"8":{"sourceFileName":"src/event.ts","qualifiedName":"EventCallback"},"9":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"10":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"11":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"12":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"13":{"sourceFileName":"src/event.ts","qualifiedName":"UnlistenFn"},"14":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"15":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"16":{"sourceFileName":"src/event.ts","qualifiedName":"EventName"},"17":{"sourceFileName":"src/event.ts","qualifiedName":"Options"},"18":{"sourceFileName":"src/event.ts","qualifiedName":"Options.target"},"19":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"20":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"21":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"22":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"23":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"24":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"25":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"26":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"27":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"28":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"29":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"30":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"31":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"32":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"33":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"34":{"sourceFileName":"src/event.ts","qualifiedName":"payload"},"35":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"36":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent"},"37":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_RESIZED"},"38":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_MOVED"},"39":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CLOSE_REQUESTED"},"40":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CREATED"},"41":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_DESTROYED"},"42":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FOCUS"},"43":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_BLUR"},"44":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_SCALE_FACTOR_CHANGED"},"45":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_THEME_CHANGED"},"46":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP"},"47":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_HOVER"},"48":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_CANCELLED"},"49":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.MENU"},"50":{"sourceFileName":"src/mocks.ts","qualifiedName":""},"51":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"52":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"53":{"sourceFileName":"src/mocks.ts","qualifiedName":"cb"},"54":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"55":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"56":{"sourceFileName":"src/mocks.ts","qualifiedName":"cmd"},"57":{"sourceFileName":"src/mocks.ts","qualifiedName":"args"},"58":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"59":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"60":{"sourceFileName":"src/mocks.ts","qualifiedName":"current"},"61":{"sourceFileName":"src/mocks.ts","qualifiedName":"additionalWindows"},"62":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"63":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"64":{"sourceFileName":"src/path.ts","qualifiedName":""},"65":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory"},"66":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Audio"},"67":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Cache"},"68":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Config"},"69":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Data"},"70":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.LocalData"},"71":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Document"},"72":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Download"},"73":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Picture"},"74":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Public"},"75":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Video"},"76":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Resource"},"77":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Temp"},"78":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppConfig"},"79":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppData"},"80":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppLocalData"},"81":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppCache"},"82":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppLog"},"83":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Desktop"},"84":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Executable"},"85":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Font"},"86":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Home"},"87":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Runtime"},"88":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Template"},"89":{"sourceFileName":"src/path.ts","qualifiedName":"appConfigDir"},"90":{"sourceFileName":"src/path.ts","qualifiedName":"appConfigDir"},"91":{"sourceFileName":"src/path.ts","qualifiedName":"appDataDir"},"92":{"sourceFileName":"src/path.ts","qualifiedName":"appDataDir"},"93":{"sourceFileName":"src/path.ts","qualifiedName":"appLocalDataDir"},"94":{"sourceFileName":"src/path.ts","qualifiedName":"appLocalDataDir"},"95":{"sourceFileName":"src/path.ts","qualifiedName":"appCacheDir"},"96":{"sourceFileName":"src/path.ts","qualifiedName":"appCacheDir"},"97":{"sourceFileName":"src/path.ts","qualifiedName":"appLogDir"},"98":{"sourceFileName":"src/path.ts","qualifiedName":"appLogDir"},"99":{"sourceFileName":"src/path.ts","qualifiedName":"audioDir"},"100":{"sourceFileName":"src/path.ts","qualifiedName":"audioDir"},"101":{"sourceFileName":"src/path.ts","qualifiedName":"cacheDir"},"102":{"sourceFileName":"src/path.ts","qualifiedName":"cacheDir"},"103":{"sourceFileName":"src/path.ts","qualifiedName":"configDir"},"104":{"sourceFileName":"src/path.ts","qualifiedName":"configDir"},"105":{"sourceFileName":"src/path.ts","qualifiedName":"dataDir"},"106":{"sourceFileName":"src/path.ts","qualifiedName":"dataDir"},"107":{"sourceFileName":"src/path.ts","qualifiedName":"desktopDir"},"108":{"sourceFileName":"src/path.ts","qualifiedName":"desktopDir"},"109":{"sourceFileName":"src/path.ts","qualifiedName":"documentDir"},"110":{"sourceFileName":"src/path.ts","qualifiedName":"documentDir"},"111":{"sourceFileName":"src/path.ts","qualifiedName":"downloadDir"},"112":{"sourceFileName":"src/path.ts","qualifiedName":"downloadDir"},"113":{"sourceFileName":"src/path.ts","qualifiedName":"executableDir"},"114":{"sourceFileName":"src/path.ts","qualifiedName":"executableDir"},"115":{"sourceFileName":"src/path.ts","qualifiedName":"fontDir"},"116":{"sourceFileName":"src/path.ts","qualifiedName":"fontDir"},"117":{"sourceFileName":"src/path.ts","qualifiedName":"homeDir"},"118":{"sourceFileName":"src/path.ts","qualifiedName":"homeDir"},"119":{"sourceFileName":"src/path.ts","qualifiedName":"localDataDir"},"120":{"sourceFileName":"src/path.ts","qualifiedName":"localDataDir"},"121":{"sourceFileName":"src/path.ts","qualifiedName":"pictureDir"},"122":{"sourceFileName":"src/path.ts","qualifiedName":"pictureDir"},"123":{"sourceFileName":"src/path.ts","qualifiedName":"publicDir"},"124":{"sourceFileName":"src/path.ts","qualifiedName":"publicDir"},"125":{"sourceFileName":"src/path.ts","qualifiedName":"resourceDir"},"126":{"sourceFileName":"src/path.ts","qualifiedName":"resourceDir"},"127":{"sourceFileName":"src/path.ts","qualifiedName":"resolveResource"},"128":{"sourceFileName":"src/path.ts","qualifiedName":"resolveResource"},"129":{"sourceFileName":"src/path.ts","qualifiedName":"resourcePath"},"130":{"sourceFileName":"src/path.ts","qualifiedName":"runtimeDir"},"131":{"sourceFileName":"src/path.ts","qualifiedName":"runtimeDir"},"132":{"sourceFileName":"src/path.ts","qualifiedName":"templateDir"},"133":{"sourceFileName":"src/path.ts","qualifiedName":"templateDir"},"134":{"sourceFileName":"src/path.ts","qualifiedName":"videoDir"},"135":{"sourceFileName":"src/path.ts","qualifiedName":"videoDir"},"136":{"sourceFileName":"src/path.ts","qualifiedName":"sep"},"137":{"sourceFileName":"src/path.ts","qualifiedName":"sep"},"138":{"sourceFileName":"src/path.ts","qualifiedName":"delimiter"},"139":{"sourceFileName":"src/path.ts","qualifiedName":"delimiter"},"140":{"sourceFileName":"src/path.ts","qualifiedName":"resolve"},"141":{"sourceFileName":"src/path.ts","qualifiedName":"resolve"},"142":{"sourceFileName":"src/path.ts","qualifiedName":"paths"},"143":{"sourceFileName":"src/path.ts","qualifiedName":"normalize"},"144":{"sourceFileName":"src/path.ts","qualifiedName":"normalize"},"145":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"146":{"sourceFileName":"src/path.ts","qualifiedName":"join"},"147":{"sourceFileName":"src/path.ts","qualifiedName":"join"},"148":{"sourceFileName":"src/path.ts","qualifiedName":"paths"},"149":{"sourceFileName":"src/path.ts","qualifiedName":"dirname"},"150":{"sourceFileName":"src/path.ts","qualifiedName":"dirname"},"151":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"152":{"sourceFileName":"src/path.ts","qualifiedName":"extname"},"153":{"sourceFileName":"src/path.ts","qualifiedName":"extname"},"154":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"155":{"sourceFileName":"src/path.ts","qualifiedName":"basename"},"156":{"sourceFileName":"src/path.ts","qualifiedName":"basename"},"157":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"158":{"sourceFileName":"src/path.ts","qualifiedName":"ext"},"159":{"sourceFileName":"src/path.ts","qualifiedName":"isAbsolute"},"160":{"sourceFileName":"src/path.ts","qualifiedName":"isAbsolute"},"161":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"162":{"sourceFileName":"src/path.ts","qualifiedName":"tempDir"},"163":{"sourceFileName":"src/path.ts","qualifiedName":"tempDir"},"164":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"165":{"sourceFileName":"src/tauri.ts","qualifiedName":""},"166":{"sourceFileName":"src/tauri.ts","qualifiedName":"InvokeArgs"},"167":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"168":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"169":{"sourceFileName":"src/tauri.ts","qualifiedName":"callback"},"170":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"171":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"172":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"173":{"sourceFileName":"src/tauri.ts","qualifiedName":"once"},"174":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"175":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__constructor"},"176":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"177":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"178":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.id"},"179":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__TAURI_CHANNEL_MARKER__"},"180":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.#onmessage"},"181":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"182":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"183":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"184":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"185":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"186":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"187":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"188":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"189":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"190":{"sourceFileName":"src/tauri.ts","qualifiedName":"handler"},"191":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"192":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"193":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"194":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"195":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"196":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"197":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"198":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.__constructor"},"199":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"200":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"201":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"202":{"sourceFileName":"src/tauri.ts","qualifiedName":"channelId"},"203":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.plugin"},"204":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.event"},"205":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.channelId"},"206":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"207":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"208":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"209":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"210":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"211":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"212":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"213":{"sourceFileName":"src/tauri.ts","qualifiedName":"cb"},"214":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"215":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"216":{"sourceFileName":"src/tauri.ts","qualifiedName":"payload"},"217":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"218":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"219":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"220":{"sourceFileName":"src/tauri.ts","qualifiedName":"cmd"},"221":{"sourceFileName":"src/tauri.ts","qualifiedName":"args"},"222":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"223":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"224":{"sourceFileName":"src/tauri.ts","qualifiedName":"filePath"},"225":{"sourceFileName":"src/tauri.ts","qualifiedName":"protocol"}}} \ No newline at end of file +{"id":0,"name":"@tauri-apps/api","variant":"project","kind":1,"flags":{},"children":[{"id":1,"name":"event","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"The event system allows you to emit events to the backend and listen to events from it.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.event`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":36,"name":"TauriEvent","variant":"declaration","kind":8,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.1.0"}]}]},"children":[{"id":49,"name":"MENU","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":59,"character":2}],"type":{"type":"literal","value":"tauri://menu"}},{"id":43,"name":"WINDOW_BLUR","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":53,"character":2}],"type":{"type":"literal","value":"tauri://blur"}},{"id":39,"name":"WINDOW_CLOSE_REQUESTED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":49,"character":2}],"type":{"type":"literal","value":"tauri://close-requested"}},{"id":40,"name":"WINDOW_CREATED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":50,"character":2}],"type":{"type":"literal","value":"tauri://window-created"}},{"id":41,"name":"WINDOW_DESTROYED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":51,"character":2}],"type":{"type":"literal","value":"tauri://destroyed"}},{"id":46,"name":"WINDOW_FILE_DROP","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":56,"character":2}],"type":{"type":"literal","value":"tauri://file-drop"}},{"id":48,"name":"WINDOW_FILE_DROP_CANCELLED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":58,"character":2}],"type":{"type":"literal","value":"tauri://file-drop-cancelled"}},{"id":47,"name":"WINDOW_FILE_DROP_HOVER","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":57,"character":2}],"type":{"type":"literal","value":"tauri://file-drop-hover"}},{"id":42,"name":"WINDOW_FOCUS","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":52,"character":2}],"type":{"type":"literal","value":"tauri://focus"}},{"id":38,"name":"WINDOW_MOVED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":48,"character":2}],"type":{"type":"literal","value":"tauri://move"}},{"id":37,"name":"WINDOW_RESIZED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":47,"character":2}],"type":{"type":"literal","value":"tauri://resize"}},{"id":44,"name":"WINDOW_SCALE_FACTOR_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":54,"character":2}],"type":{"type":"literal","value":"tauri://scale-change"}},{"id":45,"name":"WINDOW_THEME_CHANGED","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"event.ts","line":55,"character":2}],"type":{"type":"literal","value":"tauri://theme-changed"}}],"groups":[{"title":"Enumeration Members","children":[49,43,39,40,41,46,48,47,42,38,37,44,45]}],"sources":[{"fileName":"event.ts","line":46,"character":5}]},{"id":2,"name":"Event","variant":"declaration","kind":256,"flags":{},"children":[{"id":3,"name":"event","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name"}]},"sources":[{"fileName":"event.ts","line":16,"character":2}],"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":5,"name":"id","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event identifier used to unlisten"}]},"sources":[{"fileName":"event.ts","line":20,"character":2}],"type":{"type":"intrinsic","name":"number"}},{"id":6,"name":"payload","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event payload"}]},"sources":[{"fileName":"event.ts","line":22,"character":2}],"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}},{"id":4,"name":"windowLabel","variant":"declaration","kind":1024,"flags":{},"comment":{"summary":[{"kind":"text","text":"The label of the window that emitted this event."}]},"sources":[{"fileName":"event.ts","line":18,"character":2}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[3,5,6,4]}],"sources":[{"fileName":"event.ts","line":14,"character":10}],"typeParameters":[{"id":7,"name":"T","variant":"typeParam","kind":131072,"flags":{}}]},{"id":17,"name":"Options","variant":"declaration","kind":256,"flags":{},"children":[{"id":18,"name":"target","variant":"declaration","kind":1024,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"Label of the window the function targets.\n\nWhen listening to events and using this value,\nonly events triggered by the window with the given label are received.\n\nWhen emitting events, only the window with the given label will receive it."}]},"sources":[{"fileName":"event.ts","line":40,"character":2}],"type":{"type":"intrinsic","name":"string"}}],"groups":[{"title":"Properties","children":[18]}],"sources":[{"fileName":"event.ts","line":31,"character":10}]},{"id":8,"name":"EventCallback","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":25,"character":5}],"typeParameters":[{"id":12,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"type":{"type":"reflection","declaration":{"id":9,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"event.ts","line":25,"character":24}],"signatures":[{"id":10,"name":"__type","variant":"signature","kind":4096,"flags":{},"parameters":[{"id":11,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":2,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Event","package":"@tauri-apps/api"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":16,"name":"EventName","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":29,"character":5}],"type":{"type":"union","types":[{"type":"templateLiteral","head":"","tail":[[{"type":"reference","target":36,"name":"TauriEvent","package":"@tauri-apps/api"},""]]},{"type":"intersection","types":[{"type":"intrinsic","name":"string"},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"never"},{"type":"intrinsic","name":"never"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}]}]}},{"id":13,"name":"UnlistenFn","variant":"declaration","kind":4194304,"flags":{},"sources":[{"fileName":"event.ts","line":27,"character":5}],"type":{"type":"reflection","declaration":{"id":14,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"event.ts","line":27,"character":18}],"signatures":[{"id":15,"name":"__type","variant":"signature","kind":4096,"flags":{},"type":{"type":"intrinsic","name":"void"}}]}}},{"id":31,"name":"emit","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":164,"character":15}],"signatures":[{"id":32,"name":"emit","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Emits an event to the backend and all Tauri windows."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { emit } from '@tauri-apps/api/event';\nawait emit('frontend-loaded', { loggedIn: true, token: 'authToken' });\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":164,"character":0}],"parameters":[{"id":33,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"intrinsic","name":"string"}},{"id":34,"name":"payload","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"intrinsic","name":"unknown"}},{"id":35,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":19,"name":"listen","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":99,"character":15}],"signatures":[{"id":20,"name":"listen","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an event. The event can be either global or window-specific.\nSee "},{"kind":"inline-tag","tag":"@link","text":"windowLabel","target":4,"tsLinkText":""},{"kind":"text","text":" to check the event source."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { listen } from '@tauri-apps/api/event';\nconst unlisten = await listen('error', (event) => {\n console.log(`Got error in window ${event.windowLabel}, payload: ${event.payload}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":99,"character":0}],"typeParameter":[{"id":21,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":22,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":23,"name":"handler","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event handler callback."}]},"type":{"type":"reference","target":8,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}},{"id":24,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":13,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":25,"name":"once","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"event.ts","line":137,"character":15}],"signatures":[{"id":26,"name":"once","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Listen to an one-off event. See "},{"kind":"inline-tag","tag":"@link","text":"listen","target":19,"tsLinkText":""},{"kind":"text","text":" for more information."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { once } from '@tauri-apps/api/event';\ninterface LoadedPayload {\n loggedIn: boolean,\n token: string\n}\nconst unlisten = await once('loaded', (event) => {\n console.log(`App is loaded, loggedIn: ${event.payload.loggedIn}, token: ${event.payload.token}`);\n});\n\n// you need to call unlisten if your handler goes out of scope e.g. the component is unmounted\nunlisten();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving to a function to unlisten to the event.\nNote that removing the listener is required if your listener goes out of scope e.g. the component is unmounted."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"event.ts","line":137,"character":0}],"typeParameter":[{"id":27,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":28,"name":"event","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Event name. Must include only alphanumeric characters, "},{"kind":"code","text":"`-`"},{"kind":"text","text":", "},{"kind":"code","text":"`/`"},{"kind":"text","text":", "},{"kind":"code","text":"`:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`_`"},{"kind":"text","text":"."}]},"type":{"type":"reference","target":16,"name":"EventName","package":"@tauri-apps/api"}},{"id":29,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":8,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"EventCallback","package":"@tauri-apps/api"}},{"id":30,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reference","target":17,"name":"Options","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":13,"name":"UnlistenFn","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Enumerations","children":[36]},{"title":"Interfaces","children":[2,17]},{"title":"Type Aliases","children":[8,16,13]},{"title":"Functions","children":[31,19,25]}],"sources":[{"fileName":"event.ts","line":1,"character":0}]},{"id":50,"name":"mocks","variant":"declaration","kind":2,"flags":{},"children":[{"id":62,"name":"clearMocks","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":178,"character":16}],"signatures":[{"id":63,"name":"clearMocks","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Clears mocked functions/data injected by the other functions in this module.\nWhen using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.\n\n# Example\n\n"},{"kind":"code","text":"```js\nimport { mockWindows, clearMocks } from \"@tauri-apps/api/mocks\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked windows\", () => {\n mockWindows(\"main\", \"second\", \"third\");\n\n expect(window).toHaveProperty(\"__TAURI_METADATA__\")\n})\n\ntest(\"no mocked windows\", () => {\n expect(window).not.toHaveProperty(\"__TAURI_METADATA__\")\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":178,"character":0}],"type":{"type":"intrinsic","name":"void"}}]},{"id":51,"name":"mockIPC","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":80,"character":16}],"signatures":[{"id":52,"name":"mockIPC","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Intercepts all IPC requests with the given mock handler.\n\nThis function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.\n\n# Examples\n\nTesting setup using vitest:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, payload) => {\n switch (cmd) {\n case \"add\":\n return (payload.a as number) + (payload.b as number);\n default:\n break;\n }\n });\n\n expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);\n})\n```"},{"kind":"text","text":"\n\nThe callback function can also return a Promise:\n"},{"kind":"code","text":"```js\nimport { mockIPC, clearMocks } from \"@tauri-apps/api/mocks\"\nimport { invoke } from \"@tauri-apps/api/tauri\"\n\nafterEach(() => {\n clearMocks()\n})\n\ntest(\"mocked command\", () => {\n mockIPC((cmd, payload) => {\n if(cmd === \"get_data\") {\n return fetch(\"https://example.com/data.json\")\n .then((response) => response.json())\n }\n });\n\n expect(invoke('get_data')).resolves.toBe({ foo: 'bar' });\n})\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":80,"character":0}],"parameters":[{"id":53,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":54,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6}],"signatures":[{"id":55,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"mocks.ts","line":81,"character":6}],"parameters":[{"id":56,"name":"cmd","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":57,"name":"payload","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"}}],"type":{"type":"intrinsic","name":"any"}}]}}}],"type":{"type":"intrinsic","name":"void"}}]},{"id":58,"name":"mockWindows","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"mocks.ts","line":142,"character":16}],"signatures":[{"id":59,"name":"mockWindows","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Mocks one or many window labels.\nIn non-tauri context it is required to call this function *before* using the "},{"kind":"code","text":"`@tauri-apps/api/window`"},{"kind":"text","text":" module.\n\nThis function only mocks the *presence* of windows,\nwindow properties (e.g. width and height) can be mocked like regular IPC calls using the "},{"kind":"code","text":"`mockIPC`"},{"kind":"text","text":" function.\n\n# Examples\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\nimport { getCurrent } from \"@tauri-apps/api/window\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nconst win = getCurrent();\n\nwin.label // \"main\"\n```"},{"kind":"text","text":"\n\n"},{"kind":"code","text":"```js\nimport { mockWindows } from \"@tauri-apps/api/mocks\";\n\nmockWindows(\"main\", \"second\", \"third\");\n\nmockIPC((cmd, args) => {\n if (cmd === \"plugin:event|emit\") {\n console.log('emit event', args?.event, args?.payload);\n }\n});\n\nconst { emit } = await import(\"@tauri-apps/api/event\");\nawait emit('loaded'); // this will cause the mocked IPC handler to log to the console.\n```"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"mocks.ts","line":142,"character":0}],"parameters":[{"id":60,"name":"current","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"Label of window this JavaScript context is running in."}]},"type":{"type":"intrinsic","name":"string"}},{"id":61,"name":"additionalWindows","variant":"param","kind":32768,"flags":{"isRest":true},"comment":{"summary":[{"kind":"text","text":"Label of additional windows the app has."}]},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"intrinsic","name":"void"}}]}],"groups":[{"title":"Functions","children":[62,51,58]}],"sources":[{"fileName":"mocks.ts","line":1,"character":0}]},{"id":64,"name":"path","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"The path module provides utilities for working with file and directory paths.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.path`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":".\n\nIt is recommended to allowlist only the APIs you use for optimal bundle size and security."}]},"children":[{"id":65,"name":"BaseDirectory","variant":"declaration","kind":8,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"children":[{"id":81,"name":"AppCache","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":35,"character":2}],"type":{"type":"literal","value":16}},{"id":78,"name":"AppConfig","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":32,"character":2}],"type":{"type":"literal","value":13}},{"id":79,"name":"AppData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":33,"character":2}],"type":{"type":"literal","value":14}},{"id":80,"name":"AppLocalData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":34,"character":2}],"type":{"type":"literal","value":15}},{"id":82,"name":"AppLog","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":36,"character":2}],"type":{"type":"literal","value":17}},{"id":66,"name":"Audio","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":20,"character":2}],"type":{"type":"literal","value":1}},{"id":67,"name":"Cache","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":21,"character":2}],"type":{"type":"literal","value":2}},{"id":68,"name":"Config","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":22,"character":2}],"type":{"type":"literal","value":3}},{"id":69,"name":"Data","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":23,"character":2}],"type":{"type":"literal","value":4}},{"id":83,"name":"Desktop","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":38,"character":2}],"type":{"type":"literal","value":18}},{"id":71,"name":"Document","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":25,"character":2}],"type":{"type":"literal","value":6}},{"id":72,"name":"Download","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":26,"character":2}],"type":{"type":"literal","value":7}},{"id":84,"name":"Executable","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":39,"character":2}],"type":{"type":"literal","value":19}},{"id":85,"name":"Font","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":40,"character":2}],"type":{"type":"literal","value":20}},{"id":86,"name":"Home","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":41,"character":2}],"type":{"type":"literal","value":21}},{"id":70,"name":"LocalData","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":24,"character":2}],"type":{"type":"literal","value":5}},{"id":73,"name":"Picture","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":27,"character":2}],"type":{"type":"literal","value":8}},{"id":74,"name":"Public","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":28,"character":2}],"type":{"type":"literal","value":9}},{"id":76,"name":"Resource","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":30,"character":2}],"type":{"type":"literal","value":11}},{"id":87,"name":"Runtime","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":42,"character":2}],"type":{"type":"literal","value":22}},{"id":77,"name":"Temp","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":31,"character":2}],"type":{"type":"literal","value":12}},{"id":88,"name":"Template","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":43,"character":2}],"type":{"type":"literal","value":23}},{"id":75,"name":"Video","variant":"declaration","kind":16,"flags":{},"sources":[{"fileName":"path.ts","line":29,"character":2}],"type":{"type":"literal","value":10}}],"groups":[{"title":"Enumeration Members","children":[81,78,79,80,82,66,67,68,69,83,71,72,84,85,86,70,73,74,76,87,77,88,75]}],"sources":[{"fileName":"path.ts","line":19,"character":5}]},{"id":95,"name":"appCacheDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":108,"character":15}],"signatures":[{"id":96,"name":"appCacheDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's cache files.\nResolves to "},{"kind":"code","text":"`${cacheDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appCacheDir } from '@tauri-apps/api/path';\nconst appCacheDirPath = await appCacheDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":108,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":89,"name":"appConfigDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":57,"character":15}],"signatures":[{"id":90,"name":"appConfigDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's config files.\nResolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appConfigDir } from '@tauri-apps/api/path';\nconst appConfigDirPath = await appConfigDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":57,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":91,"name":"appDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":74,"character":15}],"signatures":[{"id":92,"name":"appDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's data files.\nResolves to "},{"kind":"code","text":"`${dataDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":74,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":93,"name":"appLocalDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":91,"character":15}],"signatures":[{"id":94,"name":"appLocalDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's local data files.\nResolves to "},{"kind":"code","text":"`${localDataDir}/${bundleIdentifier}`"},{"kind":"text","text":", where "},{"kind":"code","text":"`bundleIdentifier`"},{"kind":"text","text":" is the value ["},{"kind":"code","text":"`tauri.bundle.identifier`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appLocalDataDir } from '@tauri-apps/api/path';\nconst appLocalDataDirPath = await appLocalDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":91,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":97,"name":"appLogDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":520,"character":15}],"signatures":[{"id":98,"name":"appLogDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the suggested directory for your app's log files.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}/logs`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`${homeDir}/Library/Logs/{bundleIdentifier}`"},{"kind":"text","text":"\n- **Windows:** Resolves to "},{"kind":"code","text":"`${configDir}/${bundleIdentifier}/logs`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appLogDir } from '@tauri-apps/api/path';\nconst appLogDirPath = await appLogDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.2.0"}]}]},"sources":[{"fileName":"path.ts","line":520,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":99,"name":"audioDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":130,"character":15}],"signatures":[{"id":100,"name":"audioDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's audio directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_MUSIC_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Music`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Music}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { audioDir } from '@tauri-apps/api/path';\nconst audioDirPath = await audioDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":130,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":155,"name":"basename","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":653,"character":15}],"signatures":[{"id":156,"name":"basename","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the last portion of a "},{"kind":"code","text":"`path`"},{"kind":"text","text":". Trailing directory separators are ignored."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { basename, resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('app.conf');\nconst base = await basename(resourcePath);\nassert(base === 'app.conf');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":653,"character":0}],"parameters":[{"id":157,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":158,"name":"ext","variant":"param","kind":32768,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"An optional file extension to be removed from the returned path."}]},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":101,"name":"cacheDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":152,"character":15}],"signatures":[{"id":102,"name":"cacheDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's cache directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_CACHE_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.cache`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Caches`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_LocalAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { cacheDir } from '@tauri-apps/api/path';\nconst cacheDirPath = await cacheDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":152,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":103,"name":"configDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":174,"character":15}],"signatures":[{"id":104,"name":"configDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's config directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_CONFIG_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.config`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_RoamingAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { configDir } from '@tauri-apps/api/path';\nconst configDirPath = await configDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":174,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":105,"name":"dataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":196,"character":15}],"signatures":[{"id":106,"name":"dataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's data directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_RoamingAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { dataDir } from '@tauri-apps/api/path';\nconst dataDirPath = await dataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":196,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":138,"name":"delimiter","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":560,"character":9}],"signatures":[{"id":139,"name":"delimiter","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the platform-specific path segment delimiter:\n- "},{"kind":"code","text":"`;`"},{"kind":"text","text":" on Windows\n- "},{"kind":"code","text":"`:`"},{"kind":"text","text":" on POSIX"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":560,"character":0}],"type":{"type":"intrinsic","name":"string"}}]},{"id":107,"name":"desktopDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":218,"character":15}],"signatures":[{"id":108,"name":"desktopDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's desktop directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DESKTOP_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Desktop`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Desktop}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { desktopDir } from '@tauri-apps/api/path';\nconst desktopPath = await desktopDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":218,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":149,"name":"dirname","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":619,"character":15}],"signatures":[{"id":150,"name":"dirname","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the directory name of a "},{"kind":"code","text":"`path`"},{"kind":"text","text":". Trailing directory separators are ignored."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { dirname, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst dir = await dirname(appDataDirPath);\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":619,"character":0}],"parameters":[{"id":151,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":109,"name":"documentDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":240,"character":15}],"signatures":[{"id":110,"name":"documentDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's document directory."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { documentDir } from '@tauri-apps/api/path';\nconst documentDirPath = await documentDir();\n```"},{"kind":"text","text":"\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DOCUMENTS_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Documents`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Documents}`"},{"kind":"text","text":"."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":240,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":111,"name":"downloadDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":262,"character":15}],"signatures":[{"id":112,"name":"downloadDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's download directory.\n\n#### Platform-specific\n\n- **Linux**: Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_DOWNLOAD_DIR`"},{"kind":"text","text":".\n- **macOS**: Resolves to "},{"kind":"code","text":"`$HOME/Downloads`"},{"kind":"text","text":".\n- **Windows**: Resolves to "},{"kind":"code","text":"`{FOLDERID_Downloads}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { downloadDir } from '@tauri-apps/api/path';\nconst downloadDirPath = await downloadDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":262,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":113,"name":"executableDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":284,"character":15}],"signatures":[{"id":114,"name":"executableDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's executable directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_BIN_HOME/../bin`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$XDG_DATA_HOME/../bin`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/bin`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { executableDir } from '@tauri-apps/api/path';\nconst executableDirPath = await executableDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":284,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":152,"name":"extname","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":635,"character":15}],"signatures":[{"id":153,"name":"extname","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the extension of the "},{"kind":"code","text":"`path`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { extname, resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('app.conf');\nconst ext = await extname(resourcePath);\nassert(ext === 'conf');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":635,"character":0}],"parameters":[{"id":154,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":115,"name":"fontDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":306,"character":15}],"signatures":[{"id":116,"name":"fontDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's font directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME/fonts`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share/fonts`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Fonts`"},{"kind":"text","text":".\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { fontDir } from '@tauri-apps/api/path';\nconst fontDirPath = await fontDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":306,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":117,"name":"homeDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":328,"character":15}],"signatures":[{"id":118,"name":"homeDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's home directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$HOME`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Profile}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { homeDir } from '@tauri-apps/api/path';\nconst homeDirPath = await homeDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":328,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":159,"name":"isAbsolute","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":667,"character":15}],"signatures":[{"id":160,"name":"isAbsolute","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns whether the path is absolute or not."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { isAbsolute } from '@tauri-apps/api/path';\nassert(await isAbsolute('/home/tauri'));\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":667,"character":0}],"parameters":[{"id":161,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"boolean"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":146,"name":"join","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":604,"character":15}],"signatures":[{"id":147,"name":"join","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Joins all given "},{"kind":"code","text":"`path`"},{"kind":"text","text":" segments together using the platform-specific separator as a delimiter, then normalizes the resulting path."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { join, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await join(appDataDirPath, 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":604,"character":0}],"parameters":[{"id":148,"name":"paths","variant":"param","kind":32768,"flags":{"isRest":true},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":119,"name":"localDataDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":350,"character":15}],"signatures":[{"id":120,"name":"localDataDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's local data directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_DATA_HOME`"},{"kind":"text","text":" or "},{"kind":"code","text":"`$HOME/.local/share`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Library/Application Support`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_LocalAppData}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { localDataDir } from '@tauri-apps/api/path';\nconst localDataDirPath = await localDataDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":350,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":143,"name":"normalize","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":589,"character":15}],"signatures":[{"id":144,"name":"normalize","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Normalizes the given "},{"kind":"code","text":"`path`"},{"kind":"text","text":", resolving "},{"kind":"code","text":"`'..'`"},{"kind":"text","text":" and "},{"kind":"code","text":"`'.'`"},{"kind":"text","text":" segments and resolve symbolic links."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { normalize, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await normalize(appDataDirPath, '..', 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":589,"character":0}],"parameters":[{"id":145,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":121,"name":"pictureDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":372,"character":15}],"signatures":[{"id":122,"name":"pictureDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's picture directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_PICTURES_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Pictures`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Pictures}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { pictureDir } from '@tauri-apps/api/path';\nconst pictureDirPath = await pictureDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":372,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":123,"name":"publicDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":394,"character":15}],"signatures":[{"id":124,"name":"publicDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's public directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_PUBLICSHARE_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Public`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Public}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { publicDir } from '@tauri-apps/api/path';\nconst publicDirPath = await publicDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":394,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":140,"name":"resolve","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":574,"character":15}],"signatures":[{"id":141,"name":"resolve","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Resolves a sequence of "},{"kind":"code","text":"`paths`"},{"kind":"text","text":" or "},{"kind":"code","text":"`path`"},{"kind":"text","text":" segments into an absolute path."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resolve, appDataDir } from '@tauri-apps/api/path';\nconst appDataDirPath = await appDataDir();\nconst path = await resolve(appDataDirPath, '..', 'users', 'tauri', 'avatar.png');\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":574,"character":0}],"parameters":[{"id":142,"name":"paths","variant":"param","kind":32768,"flags":{"isRest":true},"type":{"type":"array","elementType":{"type":"intrinsic","name":"string"}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":127,"name":"resolveResource","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":431,"character":15}],"signatures":[{"id":128,"name":"resolveResource","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Resolve the path to a resource file."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resolveResource } from '@tauri-apps/api/path';\nconst resourcePath = await resolveResource('script.sh');\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"The full path to the resource."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":431,"character":0}],"parameters":[{"id":129,"name":"resourcePath","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The path to the resource.\nMust follow the same syntax as defined in "},{"kind":"code","text":"`tauri.conf.json > tauri > bundle > resources`"},{"kind":"text","text":", i.e. keeping subfolders and parent dir components ("},{"kind":"code","text":"`../`"},{"kind":"text","text":")."}]},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":125,"name":"resourceDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":411,"character":15}],"signatures":[{"id":126,"name":"resourceDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the application's resource directory.\nTo resolve a resource path, see the [[resolveResource | "},{"kind":"code","text":"`resolveResource API`"},{"kind":"text","text":"]]."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { resourceDir } from '@tauri-apps/api/path';\nconst resourceDirPath = await resourceDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":411,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":130,"name":"runtimeDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":454,"character":15}],"signatures":[{"id":131,"name":"runtimeDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's runtime directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to "},{"kind":"code","text":"`$XDG_RUNTIME_DIR`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Not supported."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { runtimeDir } from '@tauri-apps/api/path';\nconst runtimeDirPath = await runtimeDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":454,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":136,"name":"sep","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":549,"character":9}],"signatures":[{"id":137,"name":"sep","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the platform-specific path segment separator:\n- "},{"kind":"code","text":"`\\` on Windows\n- `"},{"kind":"text","text":"/` on POSIX"}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":549,"character":0}],"type":{"type":"intrinsic","name":"string"}}]},{"id":162,"name":"tempDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":536,"character":15}],"signatures":[{"id":163,"name":"tempDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns a temporary directory."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { tempDir } from '@tauri-apps/api/path';\nconst temp = await tempDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"path.ts","line":536,"character":0}],"parameters":[{"id":164,"name":"path","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":132,"name":"templateDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":476,"character":15}],"signatures":[{"id":133,"name":"templateDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's template directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_TEMPLATES_DIR`"},{"kind":"text","text":".\n- **macOS:** Not supported.\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Templates}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { templateDir } from '@tauri-apps/api/path';\nconst templateDirPath = await templateDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":476,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":134,"name":"videoDir","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"path.ts","line":498,"character":15}],"signatures":[{"id":135,"name":"videoDir","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Returns the path to the user's video directory.\n\n#### Platform-specific\n\n- **Linux:** Resolves to ["},{"kind":"code","text":"`xdg-user-dirs`"},{"kind":"text","text":"](https://www.freedesktop.org/wiki/Software/xdg-user-dirs/)' "},{"kind":"code","text":"`XDG_VIDEOS_DIR`"},{"kind":"text","text":".\n- **macOS:** Resolves to "},{"kind":"code","text":"`$HOME/Movies`"},{"kind":"text","text":".\n- **Windows:** Resolves to "},{"kind":"code","text":"`{FOLDERID_Videos}`"},{"kind":"text","text":"."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { videoDir } from '@tauri-apps/api/path';\nconst videoDirPath = await videoDir();\n```"}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"path.ts","line":498,"character":0}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"string"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Enumerations","children":[65]},{"title":"Functions","children":[95,89,91,93,97,99,155,101,103,105,138,107,149,109,111,113,152,115,117,159,146,119,143,121,123,140,127,125,130,136,162,132,134]}],"sources":[{"fileName":"path.ts","line":1,"character":0}]},{"id":165,"name":"tauri","variant":"declaration","kind":2,"flags":{},"comment":{"summary":[{"kind":"text","text":"Invoke your custom commands.\n\nThis package is also accessible with "},{"kind":"code","text":"`window.__TAURI__.tauri`"},{"kind":"text","text":" when ["},{"kind":"code","text":"`build.withGlobalTauri`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" is set to "},{"kind":"code","text":"`true`"},{"kind":"text","text":"."}]},"children":[{"id":176,"name":"Channel","variant":"declaration","kind":128,"flags":{},"children":[{"id":177,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":71,"character":2}],"signatures":[{"id":178,"name":"new Channel","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":71,"character":2}],"typeParameter":[{"id":179,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}],"type":{"type":"reference","target":176,"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Channel","package":"@tauri-apps/api"}}]},{"id":182,"name":"#onmessage","variant":"declaration","kind":1024,"flags":{"isPrivate":true},"sources":[{"fileName":"tauri.ts","line":67,"character":2}],"type":{"type":"reflection","declaration":{"id":183,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":67,"character":14}],"signatures":[{"id":184,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":67,"character":14}],"parameters":[{"id":185,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}},"defaultValue":"..."},{"id":181,"name":"__TAURI_CHANNEL_MARKER__","variant":"declaration","kind":1024,"flags":{"isPrivate":true,"isReadonly":true},"sources":[{"fileName":"tauri.ts","line":66,"character":19}],"type":{"type":"literal","value":true},"defaultValue":"true"},{"id":180,"name":"id","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":64,"character":2}],"type":{"type":"intrinsic","name":"number"}},{"id":186,"name":"onmessage","variant":"declaration","kind":262144,"flags":{},"sources":[{"fileName":"tauri.ts","line":77,"character":6},{"fileName":"tauri.ts","line":81,"character":6}],"getSignature":{"id":187,"name":"onmessage","variant":"signature","kind":524288,"flags":{},"sources":[{"fileName":"tauri.ts","line":81,"character":2}],"type":{"type":"reflection","declaration":{"id":188,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":81,"character":19}],"signatures":[{"id":189,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":81,"character":19}],"parameters":[{"id":190,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}},"setSignature":{"id":191,"name":"onmessage","variant":"signature","kind":1048576,"flags":{},"sources":[{"fileName":"tauri.ts","line":77,"character":2}],"parameters":[{"id":192,"name":"handler","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":193,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":77,"character":25}],"signatures":[{"id":194,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":77,"character":25}],"parameters":[{"id":195,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"intrinsic","name":"void"}}},{"id":196,"name":"toJSON","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":85,"character":2}],"signatures":[{"id":197,"name":"toJSON","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":85,"character":2}],"type":{"type":"intrinsic","name":"string"}}]}],"groups":[{"title":"Constructors","children":[177]},{"title":"Properties","children":[182,181,180]},{"title":"Accessors","children":[186]},{"title":"Methods","children":[196]}],"sources":[{"fileName":"tauri.ts","line":63,"character":6}],"typeParameters":[{"id":198,"name":"T","variant":"typeParam","kind":131072,"flags":{},"default":{"type":"intrinsic","name":"unknown"}}]},{"id":199,"name":"PluginListener","variant":"declaration","kind":128,"flags":{},"children":[{"id":200,"name":"constructor","variant":"declaration","kind":512,"flags":{},"sources":[{"fileName":"tauri.ts","line":95,"character":2}],"signatures":[{"id":201,"name":"new PluginListener","variant":"signature","kind":16384,"flags":{},"sources":[{"fileName":"tauri.ts","line":95,"character":2}],"parameters":[{"id":202,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":203,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":204,"name":"channelId","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"number"}}],"type":{"type":"reference","target":199,"name":"PluginListener","package":"@tauri-apps/api"}}]},{"id":207,"name":"channelId","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":93,"character":2}],"type":{"type":"intrinsic","name":"number"}},{"id":206,"name":"event","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":92,"character":2}],"type":{"type":"intrinsic","name":"string"}},{"id":205,"name":"plugin","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":91,"character":2}],"type":{"type":"intrinsic","name":"string"}},{"id":208,"name":"unregister","variant":"declaration","kind":2048,"flags":{},"sources":[{"fileName":"tauri.ts","line":101,"character":8}],"signatures":[{"id":209,"name":"unregister","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":101,"character":2}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"intrinsic","name":"void"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]}],"groups":[{"title":"Constructors","children":[200]},{"title":"Properties","children":[207,206,205]},{"title":"Methods","children":[208]}],"sources":[{"fileName":"tauri.ts","line":90,"character":6}]},{"id":167,"name":"InvokeOptions","variant":"declaration","kind":256,"flags":{},"comment":{"summary":[],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"children":[{"id":168,"name":"headers","variant":"declaration","kind":1024,"flags":{},"sources":[{"fileName":"tauri.ts","line":139,"character":2}],"type":{"type":"union","types":[{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"string"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.dom.d.ts","qualifiedName":"Headers"},"name":"Headers","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/API/Headers"}]}}],"groups":[{"title":"Properties","children":[168]}],"sources":[{"fileName":"tauri.ts","line":138,"character":10}]},{"id":166,"name":"InvokeArgs","variant":"declaration","kind":4194304,"flags":{},"comment":{"summary":[{"kind":"text","text":"Command arguments."}],"blockTags":[{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":133,"character":5}],"type":{"type":"union","types":[{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Record"},"typeArguments":[{"type":"intrinsic","name":"string"},{"type":"intrinsic","name":"unknown"}],"name":"Record","package":"typescript","externalUrl":"https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type"},{"type":"array","elementType":{"type":"intrinsic","name":"number"}},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"ArrayBuffer"},"name":"ArrayBuffer","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer"},{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Uint8Array"},"name":"Uint8Array","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array"}]}},{"id":210,"name":"addPluginListener","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":116,"character":15}],"signatures":[{"id":211,"name":"addPluginListener","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Adds a listener to a plugin event."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"The listener object to stop listening to the events."}]},{"tag":"@since","content":[{"kind":"text","text":"2.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":116,"character":0}],"typeParameter":[{"id":212,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":213,"name":"plugin","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":214,"name":"event","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"string"}},{"id":215,"name":"cb","variant":"param","kind":32768,"flags":{},"type":{"type":"reflection","declaration":{"id":216,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":119,"character":6}],"signatures":[{"id":217,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":119,"character":6}],"parameters":[{"id":218,"name":"payload","variant":"param","kind":32768,"flags":{},"type":{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}}],"type":{"type":"intrinsic","name":"void"}}]}}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":199,"name":"PluginListener","package":"@tauri-apps/api"}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":225,"name":"convertFileSrc","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":212,"character":9}],"signatures":[{"id":226,"name":"convertFileSrc","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Convert a device file path to an URL that can be loaded by the webview.\nNote that "},{"kind":"code","text":"`asset:`"},{"kind":"text","text":" and "},{"kind":"code","text":"`https://asset.localhost`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.security.csp`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#securityconfig.csp) in "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":".\nExample CSP value: "},{"kind":"code","text":"`\"csp\": \"default-src 'self' ipc: https://ipc.localhost; img-src 'self' asset: https://asset.localhost\"`"},{"kind":"text","text":" to use the asset protocol on image sources.\n\nAdditionally, "},{"kind":"code","text":"`asset`"},{"kind":"text","text":" must be added to ["},{"kind":"code","text":"`tauri.allowlist.protocol`"},{"kind":"text","text":"](https://tauri.app/v1/api/config/#allowlistconfig.protocol)\nin "},{"kind":"code","text":"`tauri.conf.json`"},{"kind":"text","text":" and its access scope must be defined on the "},{"kind":"code","text":"`assetScope`"},{"kind":"text","text":" array on the same "},{"kind":"code","text":"`protocol`"},{"kind":"text","text":" object."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { appDataDir, join } from '@tauri-apps/api/path';\nimport { convertFileSrc } from '@tauri-apps/api/tauri';\nconst appDataDirPath = await appDataDir();\nconst filePath = await join(appDataDirPath, 'assets/video.mp4');\nconst assetUrl = convertFileSrc(filePath);\n\nconst video = document.getElementById('my-video');\nconst source = document.createElement('source');\nsource.type = 'video/mp4';\nsource.src = assetUrl;\nvideo.appendChild(source);\nvideo.load();\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"the URL that can be used as source on the webview."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":212,"character":0}],"parameters":[{"id":227,"name":"filePath","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The file path."}]},"type":{"type":"intrinsic","name":"string"}},{"id":228,"name":"protocol","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The protocol to use. Defaults to "},{"kind":"code","text":"`asset`"},{"kind":"text","text":". You only need to set this when using a custom protocol."}]},"type":{"type":"intrinsic","name":"string"},"defaultValue":"'asset'"}],"type":{"type":"intrinsic","name":"string"}}]},{"id":219,"name":"invoke","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":157,"character":15}],"signatures":[{"id":220,"name":"invoke","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Sends a message to the backend."}],"blockTags":[{"tag":"@example","content":[{"kind":"code","text":"```typescript\nimport { invoke } from '@tauri-apps/api/tauri';\nawait invoke('login', { user: 'tauri', password: 'poiwe3h4r5ip3yrhtew9ty' });\n```"}]},{"tag":"@returns","content":[{"kind":"text","text":"A promise resolving or rejecting to the backend response."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":157,"character":0}],"typeParameter":[{"id":221,"name":"T","variant":"typeParam","kind":131072,"flags":{}}],"parameters":[{"id":222,"name":"cmd","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The command name."}]},"type":{"type":"intrinsic","name":"string"}},{"id":223,"name":"args","variant":"param","kind":32768,"flags":{},"comment":{"summary":[{"kind":"text","text":"The optional arguments to pass to the command."}]},"type":{"type":"reference","target":166,"name":"InvokeArgs","package":"@tauri-apps/api"},"defaultValue":"{}"},{"id":224,"name":"options","variant":"param","kind":32768,"flags":{"isOptional":true},"comment":{"summary":[{"kind":"text","text":"The request options."}]},"type":{"type":"reference","target":167,"name":"InvokeOptions","package":"@tauri-apps/api"}}],"type":{"type":"reference","target":{"sourceFileName":"node_modules/typescript/lib/lib.es5.d.ts","qualifiedName":"Promise"},"typeArguments":[{"type":"reference","target":-1,"name":"T","refersToTypeParameter":true}],"name":"Promise","package":"typescript","externalUrl":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise"}}]},{"id":169,"name":"transformCallback","variant":"declaration","kind":64,"flags":{},"sources":[{"fileName":"tauri.ts","line":41,"character":9}],"signatures":[{"id":170,"name":"transformCallback","variant":"signature","kind":4096,"flags":{},"comment":{"summary":[{"kind":"text","text":"Transforms a callback function to a string identifier that can be passed to the backend.\nThe backend uses the identifier to "},{"kind":"code","text":"`eval()`"},{"kind":"text","text":" the callback."}],"blockTags":[{"tag":"@returns","content":[{"kind":"text","text":"A unique identifier associated with the callback function."}]},{"tag":"@since","content":[{"kind":"text","text":"1.0.0"}]}]},"sources":[{"fileName":"tauri.ts","line":41,"character":0}],"parameters":[{"id":171,"name":"callback","variant":"param","kind":32768,"flags":{"isOptional":true},"type":{"type":"reflection","declaration":{"id":172,"name":"__type","variant":"declaration","kind":65536,"flags":{},"sources":[{"fileName":"tauri.ts","line":42,"character":13}],"signatures":[{"id":173,"name":"__type","variant":"signature","kind":4096,"flags":{},"sources":[{"fileName":"tauri.ts","line":42,"character":13}],"parameters":[{"id":174,"name":"response","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"any"}}],"type":{"type":"intrinsic","name":"void"}}]}}},{"id":175,"name":"once","variant":"param","kind":32768,"flags":{},"type":{"type":"intrinsic","name":"boolean"},"defaultValue":"false"}],"type":{"type":"intrinsic","name":"number"}}]}],"groups":[{"title":"Classes","children":[176,199]},{"title":"Interfaces","children":[167]},{"title":"Type Aliases","children":[166]},{"title":"Functions","children":[210,225,219,169]}],"sources":[{"fileName":"tauri.ts","line":1,"character":0}]}],"groups":[{"title":"Modules","children":[1,50,64,165]}],"packageName":"@tauri-apps/api","symbolIdMap":{"1":{"sourceFileName":"src/event.ts","qualifiedName":""},"2":{"sourceFileName":"src/event.ts","qualifiedName":"Event"},"3":{"sourceFileName":"src/event.ts","qualifiedName":"Event.event"},"4":{"sourceFileName":"src/event.ts","qualifiedName":"Event.windowLabel"},"5":{"sourceFileName":"src/event.ts","qualifiedName":"Event.id"},"6":{"sourceFileName":"src/event.ts","qualifiedName":"Event.payload"},"7":{"sourceFileName":"src/event.ts","qualifiedName":"Event.T"},"8":{"sourceFileName":"src/event.ts","qualifiedName":"EventCallback"},"9":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"10":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"11":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"12":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"13":{"sourceFileName":"src/event.ts","qualifiedName":"UnlistenFn"},"14":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"15":{"sourceFileName":"src/event.ts","qualifiedName":"__type"},"16":{"sourceFileName":"src/event.ts","qualifiedName":"EventName"},"17":{"sourceFileName":"src/event.ts","qualifiedName":"Options"},"18":{"sourceFileName":"src/event.ts","qualifiedName":"Options.target"},"19":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"20":{"sourceFileName":"src/event.ts","qualifiedName":"listen"},"21":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"22":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"23":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"24":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"25":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"26":{"sourceFileName":"src/event.ts","qualifiedName":"once"},"27":{"sourceFileName":"src/event.ts","qualifiedName":"T"},"28":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"29":{"sourceFileName":"src/event.ts","qualifiedName":"handler"},"30":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"31":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"32":{"sourceFileName":"src/event.ts","qualifiedName":"emit"},"33":{"sourceFileName":"src/event.ts","qualifiedName":"event"},"34":{"sourceFileName":"src/event.ts","qualifiedName":"payload"},"35":{"sourceFileName":"src/event.ts","qualifiedName":"options"},"36":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent"},"37":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_RESIZED"},"38":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_MOVED"},"39":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CLOSE_REQUESTED"},"40":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_CREATED"},"41":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_DESTROYED"},"42":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FOCUS"},"43":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_BLUR"},"44":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_SCALE_FACTOR_CHANGED"},"45":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_THEME_CHANGED"},"46":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP"},"47":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_HOVER"},"48":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.WINDOW_FILE_DROP_CANCELLED"},"49":{"sourceFileName":"src/event.ts","qualifiedName":"TauriEvent.MENU"},"50":{"sourceFileName":"src/mocks.ts","qualifiedName":""},"51":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"52":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockIPC"},"53":{"sourceFileName":"src/mocks.ts","qualifiedName":"cb"},"54":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"55":{"sourceFileName":"src/mocks.ts","qualifiedName":"__type"},"56":{"sourceFileName":"src/mocks.ts","qualifiedName":"cmd"},"57":{"sourceFileName":"src/mocks.ts","qualifiedName":"payload"},"58":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"59":{"sourceFileName":"src/mocks.ts","qualifiedName":"mockWindows"},"60":{"sourceFileName":"src/mocks.ts","qualifiedName":"current"},"61":{"sourceFileName":"src/mocks.ts","qualifiedName":"additionalWindows"},"62":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"63":{"sourceFileName":"src/mocks.ts","qualifiedName":"clearMocks"},"64":{"sourceFileName":"src/path.ts","qualifiedName":""},"65":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory"},"66":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Audio"},"67":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Cache"},"68":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Config"},"69":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Data"},"70":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.LocalData"},"71":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Document"},"72":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Download"},"73":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Picture"},"74":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Public"},"75":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Video"},"76":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Resource"},"77":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Temp"},"78":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppConfig"},"79":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppData"},"80":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppLocalData"},"81":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppCache"},"82":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.AppLog"},"83":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Desktop"},"84":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Executable"},"85":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Font"},"86":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Home"},"87":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Runtime"},"88":{"sourceFileName":"src/path.ts","qualifiedName":"BaseDirectory.Template"},"89":{"sourceFileName":"src/path.ts","qualifiedName":"appConfigDir"},"90":{"sourceFileName":"src/path.ts","qualifiedName":"appConfigDir"},"91":{"sourceFileName":"src/path.ts","qualifiedName":"appDataDir"},"92":{"sourceFileName":"src/path.ts","qualifiedName":"appDataDir"},"93":{"sourceFileName":"src/path.ts","qualifiedName":"appLocalDataDir"},"94":{"sourceFileName":"src/path.ts","qualifiedName":"appLocalDataDir"},"95":{"sourceFileName":"src/path.ts","qualifiedName":"appCacheDir"},"96":{"sourceFileName":"src/path.ts","qualifiedName":"appCacheDir"},"97":{"sourceFileName":"src/path.ts","qualifiedName":"appLogDir"},"98":{"sourceFileName":"src/path.ts","qualifiedName":"appLogDir"},"99":{"sourceFileName":"src/path.ts","qualifiedName":"audioDir"},"100":{"sourceFileName":"src/path.ts","qualifiedName":"audioDir"},"101":{"sourceFileName":"src/path.ts","qualifiedName":"cacheDir"},"102":{"sourceFileName":"src/path.ts","qualifiedName":"cacheDir"},"103":{"sourceFileName":"src/path.ts","qualifiedName":"configDir"},"104":{"sourceFileName":"src/path.ts","qualifiedName":"configDir"},"105":{"sourceFileName":"src/path.ts","qualifiedName":"dataDir"},"106":{"sourceFileName":"src/path.ts","qualifiedName":"dataDir"},"107":{"sourceFileName":"src/path.ts","qualifiedName":"desktopDir"},"108":{"sourceFileName":"src/path.ts","qualifiedName":"desktopDir"},"109":{"sourceFileName":"src/path.ts","qualifiedName":"documentDir"},"110":{"sourceFileName":"src/path.ts","qualifiedName":"documentDir"},"111":{"sourceFileName":"src/path.ts","qualifiedName":"downloadDir"},"112":{"sourceFileName":"src/path.ts","qualifiedName":"downloadDir"},"113":{"sourceFileName":"src/path.ts","qualifiedName":"executableDir"},"114":{"sourceFileName":"src/path.ts","qualifiedName":"executableDir"},"115":{"sourceFileName":"src/path.ts","qualifiedName":"fontDir"},"116":{"sourceFileName":"src/path.ts","qualifiedName":"fontDir"},"117":{"sourceFileName":"src/path.ts","qualifiedName":"homeDir"},"118":{"sourceFileName":"src/path.ts","qualifiedName":"homeDir"},"119":{"sourceFileName":"src/path.ts","qualifiedName":"localDataDir"},"120":{"sourceFileName":"src/path.ts","qualifiedName":"localDataDir"},"121":{"sourceFileName":"src/path.ts","qualifiedName":"pictureDir"},"122":{"sourceFileName":"src/path.ts","qualifiedName":"pictureDir"},"123":{"sourceFileName":"src/path.ts","qualifiedName":"publicDir"},"124":{"sourceFileName":"src/path.ts","qualifiedName":"publicDir"},"125":{"sourceFileName":"src/path.ts","qualifiedName":"resourceDir"},"126":{"sourceFileName":"src/path.ts","qualifiedName":"resourceDir"},"127":{"sourceFileName":"src/path.ts","qualifiedName":"resolveResource"},"128":{"sourceFileName":"src/path.ts","qualifiedName":"resolveResource"},"129":{"sourceFileName":"src/path.ts","qualifiedName":"resourcePath"},"130":{"sourceFileName":"src/path.ts","qualifiedName":"runtimeDir"},"131":{"sourceFileName":"src/path.ts","qualifiedName":"runtimeDir"},"132":{"sourceFileName":"src/path.ts","qualifiedName":"templateDir"},"133":{"sourceFileName":"src/path.ts","qualifiedName":"templateDir"},"134":{"sourceFileName":"src/path.ts","qualifiedName":"videoDir"},"135":{"sourceFileName":"src/path.ts","qualifiedName":"videoDir"},"136":{"sourceFileName":"src/path.ts","qualifiedName":"sep"},"137":{"sourceFileName":"src/path.ts","qualifiedName":"sep"},"138":{"sourceFileName":"src/path.ts","qualifiedName":"delimiter"},"139":{"sourceFileName":"src/path.ts","qualifiedName":"delimiter"},"140":{"sourceFileName":"src/path.ts","qualifiedName":"resolve"},"141":{"sourceFileName":"src/path.ts","qualifiedName":"resolve"},"142":{"sourceFileName":"src/path.ts","qualifiedName":"paths"},"143":{"sourceFileName":"src/path.ts","qualifiedName":"normalize"},"144":{"sourceFileName":"src/path.ts","qualifiedName":"normalize"},"145":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"146":{"sourceFileName":"src/path.ts","qualifiedName":"join"},"147":{"sourceFileName":"src/path.ts","qualifiedName":"join"},"148":{"sourceFileName":"src/path.ts","qualifiedName":"paths"},"149":{"sourceFileName":"src/path.ts","qualifiedName":"dirname"},"150":{"sourceFileName":"src/path.ts","qualifiedName":"dirname"},"151":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"152":{"sourceFileName":"src/path.ts","qualifiedName":"extname"},"153":{"sourceFileName":"src/path.ts","qualifiedName":"extname"},"154":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"155":{"sourceFileName":"src/path.ts","qualifiedName":"basename"},"156":{"sourceFileName":"src/path.ts","qualifiedName":"basename"},"157":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"158":{"sourceFileName":"src/path.ts","qualifiedName":"ext"},"159":{"sourceFileName":"src/path.ts","qualifiedName":"isAbsolute"},"160":{"sourceFileName":"src/path.ts","qualifiedName":"isAbsolute"},"161":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"162":{"sourceFileName":"src/path.ts","qualifiedName":"tempDir"},"163":{"sourceFileName":"src/path.ts","qualifiedName":"tempDir"},"164":{"sourceFileName":"src/path.ts","qualifiedName":"path"},"165":{"sourceFileName":"src/tauri.ts","qualifiedName":""},"166":{"sourceFileName":"src/tauri.ts","qualifiedName":"InvokeArgs"},"167":{"sourceFileName":"src/tauri.ts","qualifiedName":"InvokeOptions"},"168":{"sourceFileName":"src/tauri.ts","qualifiedName":"InvokeOptions.headers"},"169":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"170":{"sourceFileName":"src/tauri.ts","qualifiedName":"transformCallback"},"171":{"sourceFileName":"src/tauri.ts","qualifiedName":"callback"},"172":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"173":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"174":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"175":{"sourceFileName":"src/tauri.ts","qualifiedName":"once"},"176":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"177":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__constructor"},"178":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel"},"179":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"180":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.id"},"181":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.__TAURI_CHANNEL_MARKER__"},"182":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.#onmessage"},"183":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"184":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"185":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"186":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"187":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"188":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"189":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"190":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"191":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.onmessage"},"192":{"sourceFileName":"src/tauri.ts","qualifiedName":"handler"},"193":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"194":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"195":{"sourceFileName":"src/tauri.ts","qualifiedName":"response"},"196":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"197":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.toJSON"},"198":{"sourceFileName":"src/tauri.ts","qualifiedName":"Channel.T"},"199":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"200":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.__constructor"},"201":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener"},"202":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"203":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"204":{"sourceFileName":"src/tauri.ts","qualifiedName":"channelId"},"205":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.plugin"},"206":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.event"},"207":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.channelId"},"208":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"209":{"sourceFileName":"src/tauri.ts","qualifiedName":"PluginListener.unregister"},"210":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"211":{"sourceFileName":"src/tauri.ts","qualifiedName":"addPluginListener"},"212":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"213":{"sourceFileName":"src/tauri.ts","qualifiedName":"plugin"},"214":{"sourceFileName":"src/tauri.ts","qualifiedName":"event"},"215":{"sourceFileName":"src/tauri.ts","qualifiedName":"cb"},"216":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"217":{"sourceFileName":"src/tauri.ts","qualifiedName":"__type"},"218":{"sourceFileName":"src/tauri.ts","qualifiedName":"payload"},"219":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"220":{"sourceFileName":"src/tauri.ts","qualifiedName":"invoke"},"221":{"sourceFileName":"src/tauri.ts","qualifiedName":"T"},"222":{"sourceFileName":"src/tauri.ts","qualifiedName":"cmd"},"223":{"sourceFileName":"src/tauri.ts","qualifiedName":"args"},"224":{"sourceFileName":"src/tauri.ts","qualifiedName":"options"},"225":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"226":{"sourceFileName":"src/tauri.ts","qualifiedName":"convertFileSrc"},"227":{"sourceFileName":"src/tauri.ts","qualifiedName":"filePath"},"228":{"sourceFileName":"src/tauri.ts","qualifiedName":"protocol"}}} \ No newline at end of file diff --git a/tooling/api/src/mocks.ts b/tooling/api/src/mocks.ts index 8cea9e22ed7e..ce5a93f9bb80 100644 --- a/tooling/api/src/mocks.ts +++ b/tooling/api/src/mocks.ts @@ -41,10 +41,10 @@ interface IPCMessage { * }) * * test("mocked command", () => { - * mockIPC((cmd, args) => { + * mockIPC((cmd, payload) => { * switch (cmd) { * case "add": - * return (args.a as number) + (args.b as number); + * return (payload.a as number) + (payload.b as number); * default: * break; * } @@ -64,7 +64,7 @@ interface IPCMessage { * }) * * test("mocked command", () => { - * mockIPC((cmd, args) => { + * mockIPC((cmd, payload) => { * if(cmd === "get_data") { * return fetch("https://example.com/data.json") * .then((response) => response.json()) @@ -78,19 +78,19 @@ interface IPCMessage { * @since 1.0.0 */ export function mockIPC( - cb: (cmd: string, args: Record) => any | Promise + cb: (cmd: string, payload: Record) => any | Promise ): void { // eslint-disable-next-line @typescript-eslint/no-misused-promises window.__TAURI_IPC__ = async ({ cmd, callback, error, - ...args + payload }: IPCMessage) => { try { // @ts-expect-error The function key is dynamic and therefore not typed // eslint-disable-next-line @typescript-eslint/no-unsafe-call - window[`_${callback}`](await cb(cmd, args)) + window[`_${callback}`](await cb(cmd, payload)) } catch (err) { // @ts-expect-error The function key is dynamic and therefore not typed // eslint-disable-next-line @typescript-eslint/no-unsafe-call diff --git a/tooling/api/src/path.ts b/tooling/api/src/path.ts index 93d0b468639b..e87f0480188f 100644 --- a/tooling/api/src/path.ts +++ b/tooling/api/src/path.ts @@ -43,17 +43,6 @@ enum BaseDirectory { Template } -declare global { - interface Window { - __TAURI__: { - path: { - __sep: string - __delimiter: string - } - } - } -} - /** * Returns the path to the suggested directory for your app's config files. * Resolves to `${configDir}/${bundleIdentifier}`, where `bundleIdentifier` is the value [`tauri.bundle.identifier`](https://tauri.app/v1/api/config/#bundleconfig.identifier) is configured in `tauri.conf.json`. diff --git a/tooling/api/src/tauri.ts b/tooling/api/src/tauri.ts index d8ca1aa70e44..733039733db0 100644 --- a/tooling/api/src/tauri.ts +++ b/tooling/api/src/tauri.ts @@ -11,12 +11,17 @@ /** @ignore */ declare global { - // eslint-disable-next-line @typescript-eslint/no-unused-vars interface Window { - __TAURI_IPC__: (message: any) => void - ipc: { - postMessage: (args: string) => void + __TAURI__: { + path: { + __sep: string + __delimiter: string + } + + convertFileSrc: (src: string, protocol: string) => string } + + __TAURI_IPC__: (message: any) => void } } @@ -125,7 +130,14 @@ async function addPluginListener( * * @since 1.0.0 */ -type InvokeArgs = Record +type InvokeArgs = Record | number[] | ArrayBuffer | Uint8Array + +/** + * @since 2.0.0 + */ +interface InvokeOptions { + headers: Headers | Record +} /** * Sends a message to the backend. @@ -137,11 +149,16 @@ type InvokeArgs = Record * * @param cmd The command name. * @param args The optional arguments to pass to the command. + * @param options The request options. * @return A promise resolving or rejecting to the backend response. * * @since 1.0.0 */ -async function invoke(cmd: string, args: InvokeArgs = {}): Promise { +async function invoke( + cmd: string, + args: InvokeArgs = {}, + options?: InvokeOptions +): Promise { return new Promise((resolve, reject) => { const callback = transformCallback((e: T) => { resolve(e) @@ -156,7 +173,8 @@ async function invoke(cmd: string, args: InvokeArgs = {}): Promise { cmd, callback, error, - ...args + payload: args, + options }) }) } @@ -164,7 +182,7 @@ async function invoke(cmd: string, args: InvokeArgs = {}): Promise { /** * Convert a device file path to an URL that can be loaded by the webview. * Note that `asset:` and `https://asset.localhost` must be added to [`tauri.security.csp`](https://tauri.app/v1/api/config/#securityconfig.csp) in `tauri.conf.json`. - * Example CSP value: `"csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost"` to use the asset protocol on image sources. + * Example CSP value: `"csp": "default-src 'self' ipc: https://ipc.localhost; img-src 'self' asset: https://asset.localhost"` to use the asset protocol on image sources. * * Additionally, `asset` must be added to [`tauri.allowlist.protocol`](https://tauri.app/v1/api/config/#allowlistconfig.protocol) * in `tauri.conf.json` and its access scope must be defined on the `assetScope` array on the same `protocol` object. @@ -192,13 +210,10 @@ async function invoke(cmd: string, args: InvokeArgs = {}): Promise { * @since 1.0.0 */ function convertFileSrc(filePath: string, protocol = 'asset'): string { - const path = encodeURIComponent(filePath) - return navigator.userAgent.includes('Windows') - ? `https://${protocol}.localhost/${path}` - : `${protocol}://localhost/${path}` + return window.__TAURI__.convertFileSrc(filePath, protocol) } -export type { InvokeArgs } +export type { InvokeArgs, InvokeOptions } export { transformCallback, diff --git a/tooling/bench/tests/cpu_intensive/src-tauri/tauri.conf.json b/tooling/bench/tests/cpu_intensive/src-tauri/tauri.conf.json index aa6e927d594e..eae6098e9823 100644 --- a/tooling/bench/tests/cpu_intensive/src-tauri/tauri.conf.json +++ b/tooling/bench/tests/cpu_intensive/src-tauri/tauri.conf.json @@ -43,7 +43,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/tooling/bench/tests/files_transfer/src-tauri/src/main.rs b/tooling/bench/tests/files_transfer/src-tauri/src/main.rs index da93689e499e..34c294c26a3f 100644 --- a/tooling/bench/tests/files_transfer/src-tauri/src/main.rs +++ b/tooling/bench/tests/files_transfer/src-tauri/src/main.rs @@ -5,7 +5,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use std::fs::read; -use tauri::{command, path::BaseDirectory, AppHandle, Manager, Runtime}; +use tauri::{command, ipc::Response, path::BaseDirectory, AppHandle, Manager, Runtime}; #[command] fn app_should_close(exit_code: i32) { @@ -13,13 +13,13 @@ fn app_should_close(exit_code: i32) { } #[command] -async fn read_file(app: AppHandle) -> Result, String> { +async fn read_file(app: AppHandle) -> Result { let path = app .path() .resolve(".tauri_3mb.json", BaseDirectory::Home) .map_err(|e| e.to_string())?; let contents = read(&path).map_err(|e| e.to_string())?; - Ok(contents) + Ok(Response::new(contents)) } fn main() { diff --git a/tooling/bench/tests/files_transfer/src-tauri/tauri.conf.json b/tooling/bench/tests/files_transfer/src-tauri/tauri.conf.json index aa6e927d594e..eae6098e9823 100644 --- a/tooling/bench/tests/files_transfer/src-tauri/tauri.conf.json +++ b/tooling/bench/tests/files_transfer/src-tauri/tauri.conf.json @@ -43,7 +43,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/tooling/bench/tests/helloworld/src-tauri/tauri.conf.json b/tooling/bench/tests/helloworld/src-tauri/tauri.conf.json index aa6e927d594e..eae6098e9823 100644 --- a/tooling/bench/tests/helloworld/src-tauri/tauri.conf.json +++ b/tooling/bench/tests/helloworld/src-tauri/tauri.conf.json @@ -43,7 +43,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file diff --git a/tooling/cli/src/migrate/config.rs b/tooling/cli/src/migrate/config.rs index 7312359212ea..a9ea92327b91 100644 --- a/tooling/cli/src/migrate/config.rs +++ b/tooling/cli/src/migrate/config.rs @@ -47,6 +47,13 @@ fn migrate_config(config: &mut Value) -> Result<()> { process_allowlist(tauri_config, &mut plugins, allowlist)?; } + if let Some(security) = tauri_config + .get_mut("security") + .and_then(|c| c.as_object_mut()) + { + process_security(security)?; + } + // cli if let Some(cli) = tauri_config.remove("cli") { process_cli(&mut plugins, cli)?; @@ -64,6 +71,44 @@ fn migrate_config(config: &mut Value) -> Result<()> { Ok(()) } +fn process_security(security: &mut Map) -> Result<()> { + // migrate CSP: add `ipc:` to `connect-src` + if let Some(csp_value) = security.remove("csp") { + let csp = if csp_value.is_null() { + csp_value + } else { + let mut csp: tauri_utils_v1::config::Csp = serde_json::from_value(csp_value)?; + match &mut csp { + tauri_utils_v1::config::Csp::Policy(csp) => { + if csp.contains("connect-src") { + *csp = csp.replace("connect-src", "connect-src ipc: https://ipc.localhost"); + } else { + *csp = format!("{csp}; connect-src ipc: https://ipc.localhost"); + } + } + tauri_utils_v1::config::Csp::DirectiveMap(csp) => { + if let Some(connect_src) = csp.get_mut("connect-src") { + if !connect_src.contains("ipc: https://ipc.localhost") { + connect_src.push("ipc: https://ipc.localhost"); + } + } else { + csp.insert( + "connect-src".into(), + tauri_utils_v1::config::CspDirectiveSources::List(vec![ + "ipc: https://ipc.localhost".to_string() + ]), + ); + } + } + } + serde_json::to_value(csp)? + }; + + security.insert("csp".into(), csp); + } + Ok(()) +} + fn process_allowlist( tauri_config: &mut Map, plugins: &mut Map, @@ -142,8 +187,19 @@ fn process_updater( #[cfg(test)] mod test { + fn migrate(original: &serde_json::Value) -> serde_json::Value { + let mut migrated = original.clone(); + super::migrate_config(&mut migrated).expect("failed to migrate config"); + + if let Err(e) = serde_json::from_value::(migrated.clone()) { + panic!("migrated config is not valid: {e}"); + } + + migrated + } + #[test] - fn migrate() { + fn migrate_full() { let original = serde_json::json!({ "tauri": { "bundle": { @@ -199,16 +255,14 @@ mod test { "http": { "scope": ["http://localhost:3003/"] } + }, + "security": { + "csp": "default-src: 'self' tauri:" } } }); - let mut migrated = original.clone(); - super::migrate_config(&mut migrated).expect("failed to migrate config"); - - if let Err(e) = serde_json::from_value::(migrated.clone()) { - panic!("migrated config is not valid: {e}"); - } + let migrated = migrate(&original); // bundle > updater assert_eq!( @@ -268,5 +322,105 @@ mod test { migrated["tauri"]["security"]["assetProtocol"]["scope"], original["tauri"]["allowlist"]["protocol"]["assetScope"] ); + + // security CSP + assert_eq!( + migrated["tauri"]["security"]["csp"], + format!( + "{}; connect-src ipc: https://ipc.localhost", + original["tauri"]["security"]["csp"].as_str().unwrap() + ) + ); + } + + #[test] + fn migrate_csp_object() { + let original = serde_json::json!({ + "tauri": { + "security": { + "csp": { + "default-src": ["self", "tauri:"] + } + } + } + }); + + let migrated = migrate(&original); + + assert_eq!( + migrated["tauri"]["security"]["csp"]["default-src"], + original["tauri"]["security"]["csp"]["default-src"] + ); + assert!(migrated["tauri"]["security"]["csp"]["connect-src"] + .as_array() + .expect("connect-src isn't an array") + .contains(&"ipc: https://ipc.localhost".into())); + } + + #[test] + fn migrate_csp_existing_connect_src_string() { + let original = serde_json::json!({ + "tauri": { + "security": { + "csp": { + "default-src": ["self", "tauri:"], + "connect-src": "self" + } + } + } + }); + + let migrated = migrate(&original); + + assert_eq!( + migrated["tauri"]["security"]["csp"]["default-src"], + original["tauri"]["security"]["csp"]["default-src"] + ); + assert_eq!( + migrated["tauri"]["security"]["csp"]["connect-src"] + .as_str() + .expect("connect-src isn't a string"), + format!( + "{} ipc: https://ipc.localhost", + original["tauri"]["security"]["csp"]["connect-src"] + .as_str() + .unwrap() + ) + ); + } + + #[test] + fn migrate_csp_existing_connect_src_array() { + let original = serde_json::json!({ + "tauri": { + "security": { + "csp": { + "default-src": ["self", "tauri:"], + "connect-src": ["self", "asset:"] + } + } + } + }); + + let migrated = migrate(&original); + + assert_eq!( + migrated["tauri"]["security"]["csp"]["default-src"], + original["tauri"]["security"]["csp"]["default-src"] + ); + + let migrated_connect_src = migrated["tauri"]["security"]["csp"]["connect-src"] + .as_array() + .expect("connect-src isn't an array"); + let original_connect_src = original["tauri"]["security"]["csp"]["connect-src"] + .as_array() + .unwrap(); + assert!( + migrated_connect_src + .iter() + .zip(original_connect_src.iter()) + .all(|(a, b)| a == b), + "connect-src migration failed" + ); } } diff --git a/tooling/cli/templates/plugin/__example-basic/vanilla/src-tauri/tauri.conf.json b/tooling/cli/templates/plugin/__example-basic/vanilla/src-tauri/tauri.conf.json index 36149bb456af..787dadd50f1b 100644 --- a/tooling/cli/templates/plugin/__example-basic/vanilla/src-tauri/tauri.conf.json +++ b/tooling/cli/templates/plugin/__example-basic/vanilla/src-tauri/tauri.conf.json @@ -50,7 +50,7 @@ } ], "security": { - "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'" + "csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'; connect-src ipc: https://ipc.localhost" } } -} +} \ No newline at end of file