收口桌面运行时桥接边界

将 Tauri host.getRuntime 回包组装收口到 runtime 模块

让桌面 dispatch 只委托运行时请求

同步桌面壳结构门禁、架构文档和共享记忆
This commit is contained in:
2026-06-19 23:29:08 +08:00
parent ffe7bda460
commit 582fb20347
8 changed files with 150 additions and 120 deletions
+26 -4
View File
@@ -118,6 +118,14 @@ const desktopHostBridgeNotificationsSource = fs.readFileSync(
desktopHostBridgeNotificationsPath,
'utf8',
);
const desktopHostBridgeRuntimePath = new URL(
'../src-tauri/src/host_bridge/runtime.rs',
import.meta.url,
);
const desktopHostBridgeRuntimeSource = fs.readFileSync(
desktopHostBridgeRuntimePath,
'utf8',
);
const desktopHostBridgeSharePath = new URL(
'../src-tauri/src/host_bridge/share.rs',
import.meta.url,
@@ -252,6 +260,7 @@ const expectedDesktopHostBridgeRustFiles = [
'network.rs',
'notifications.rs',
'protocol.rs',
'runtime.rs',
'share.rs',
'title.rs',
];
@@ -1708,21 +1717,33 @@ if (!rustHostSource.includes('host_version: env!("CARGO_PKG_VERSION")')) {
}
for (const runtimeSnippet of [
'fn desktop_runtime() -> HostBridgeRuntime',
'pub(crate) fn desktop_runtime() -> HostBridgeRuntime',
'shell: "tauri_desktop"',
'platform: desktop_platform()',
'host_version: env!("CARGO_PKG_VERSION")',
'bridge_version: HOST_BRIDGE_VERSION',
'capabilities: capabilities()',
'"host.getRuntime" => ok(request.id, json!(desktop_runtime()))',
]) {
if (!desktopHostBridgeDispatchSource.includes(runtimeSnippet)) {
if (!desktopHostBridgeRuntimeSource.includes(runtimeSnippet)) {
throw new Error(`desktop shell runtime response missing ${runtimeSnippet}`);
}
}
if (
!desktopHostBridgeDispatchSource.includes(
'"host.getRuntime" => ok(request.id, json!(desktop_runtime()))',
) ||
desktopHostBridgeDispatchSource.includes('HostBridgeRuntime') ||
desktopHostBridgeDispatchSource.includes('HOST_BRIDGE_VERSION') ||
desktopHostBridgeDispatchSource.includes('capabilities()') ||
desktopHostBridgeDispatchSource.includes('desktop_platform()') ||
desktopHostBridgeDispatchSource.includes('env!("CARGO_PKG_VERSION")')
) {
throw new Error('desktop shell dispatch must delegate host.getRuntime to runtime.rs');
}
if (desktopHostBridgeDispatchSource.includes('json!(HostBridgeRuntime {')) {
throw new Error('desktop shell host.getRuntime must be built by desktop_runtime()');
throw new Error('desktop shell host.getRuntime must be built by runtime.rs');
}
if (
@@ -1986,6 +2007,7 @@ const expectedRustHostBridgeFiles = [
'network.rs',
'notifications.rs',
'protocol.rs',
'runtime.rs',
'share.rs',
'title.rs',
];
@@ -1,6 +1,5 @@
use crate::host_bridge::appearance::desktop_appearance_color_scheme;
use crate::host_bridge::badge::set_desktop_app_badge_count;
use crate::host_bridge::capabilities::capabilities;
use crate::host_bridge::clipboard::{
read_desktop_host_bridge_clipboard_text, write_desktop_host_bridge_clipboard_text,
};
@@ -17,26 +16,15 @@ use crate::host_bridge::navigation::{
use crate::host_bridge::network::resolve_desktop_host_bridge_network_status;
use crate::host_bridge::notifications::show_desktop_local_notification;
use crate::host_bridge::protocol::{
failed, ok, validate_request, HostBridgeRequest, HostBridgeResponse, HostBridgeRuntime,
HOST_BRIDGE_VERSION,
failed, ok, validate_request, HostBridgeRequest, HostBridgeResponse,
};
use crate::host_bridge::runtime::desktop_runtime;
use crate::host_bridge::share::{
open_desktop_host_bridge_share, set_desktop_host_bridge_share_target,
};
use crate::host_bridge::title::set_desktop_host_bridge_window_title;
use crate::shell::webview::desktop_platform;
use serde_json::json;
fn desktop_runtime() -> HostBridgeRuntime {
HostBridgeRuntime {
shell: "tauri_desktop",
platform: desktop_platform(),
host_version: env!("CARGO_PKG_VERSION"),
bridge_version: HOST_BRIDGE_VERSION,
capabilities: capabilities(),
}
}
pub(crate) fn resolve_host_bridge_request(request: HostBridgeRequest) -> HostBridgeResponse {
if let Some(response) = validate_request(&request) {
return response;
@@ -98,101 +86,6 @@ mod tests {
use super::*;
use crate::host_bridge::protocol::request;
#[test]
fn runtime_response_reports_tauri_shell() {
let response = resolve_host_bridge_request(request("host.getRuntime"));
assert!(response.ok);
let result = response.result.expect("runtime result");
assert_eq!(result["shell"], "tauri_desktop");
assert_eq!(result["bridgeVersion"], HOST_BRIDGE_VERSION);
assert_eq!(result["capabilities"], json!(capabilities()));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("appearance.getColorScheme")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("host.events")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.lifecycle")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("network.status")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("network.statusChanged")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("share.open")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("share.setTarget")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("navigation.openNativePage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.reloadWebView")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.setTitle")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.setBadgeCount")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("clipboard.readText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importDocument")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportImage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importImage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importAudio")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportAudio")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.imageDropped")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("notification.showLocal")));
}
#[test]
fn unsupported_method_is_explicit() {
for method in ["auth.requestLogin", "payment.request", "scanner.scanQrCode"] {
@@ -8,6 +8,7 @@ mod navigation;
mod network;
mod notifications;
pub(crate) mod protocol;
mod runtime;
mod share;
mod title;
@@ -0,0 +1,112 @@
use crate::host_bridge::capabilities::capabilities;
use crate::host_bridge::protocol::{HostBridgeRuntime, HOST_BRIDGE_VERSION};
use crate::shell::webview::desktop_platform;
pub(crate) fn desktop_runtime() -> HostBridgeRuntime {
HostBridgeRuntime {
shell: "tauri_desktop",
platform: desktop_platform(),
host_version: env!("CARGO_PKG_VERSION"),
bridge_version: HOST_BRIDGE_VERSION,
capabilities: capabilities(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{json, to_value};
#[test]
fn runtime_response_reports_tauri_shell() {
let result = to_value(desktop_runtime()).expect("runtime result");
assert_eq!(result["shell"], "tauri_desktop");
assert_eq!(result["bridgeVersion"], HOST_BRIDGE_VERSION);
assert_eq!(result["capabilities"], json!(capabilities()));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("appearance.getColorScheme")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("host.events")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.lifecycle")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("network.status")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("network.statusChanged")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("share.open")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("share.setTarget")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("navigation.openNativePage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.reloadWebView")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.setTitle")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("app.setBadgeCount")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("clipboard.readText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importText")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importDocument")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportImage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importImage")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.importAudio")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.exportAudio")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("file.imageDropped")));
assert!(result["capabilities"]
.as_array()
.unwrap()
.contains(&json!("notification.showLocal")));
}
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -308,6 +308,7 @@ const expectedDesktopHostBridgeRustFiles = [
'network.rs',
'notifications.rs',
'protocol.rs',
'runtime.rs',
'share.rs',
'title.rs',
];