收口桌面运行时桥接边界

将 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")));
}
}